diff options
91 files changed, 4630 insertions, 1670 deletions
diff --git a/Documentation/CodingStyle b/Documentation/CodingStyle index a667eb1fc26..7f1730f1a1a 100644 --- a/Documentation/CodingStyle +++ b/Documentation/CodingStyle @@ -633,12 +633,27 @@ covers RTL which is used frequently with assembly language in the kernel. Kernel developers like to be seen as literate. Do mind the spelling of kernel messages to make a good impression. Do not use crippled -words like "dont" and use "do not" or "don't" instead. +words like "dont"; use "do not" or "don't" instead. Make the messages +concise, clear, and unambiguous. Kernel messages do not have to be terminated with a period. Printing numbers in parentheses (%d) adds no value and should be avoided. +There are a number of driver model diagnostic macros in <linux/device.h> +which you should use to make sure messages are matched to the right device +and driver, and are tagged with the right level: dev_err(), dev_warn(), +dev_info(), and so forth. For messages that aren't associated with a +particular device, <linux/kernel.h> defines pr_debug() and pr_info(). + +Coming up with good debugging messages can be quite a challenge; and once +you have them, they can be a huge help for remote troubleshooting. Such +messages should be compiled out when the DEBUG symbol is not defined (that +is, by default they are not included). When you use dev_dbg() or pr_debug(), +that's automatic. Many subsystems have Kconfig options to turn on -DDEBUG. +A related convention uses VERBOSE_DEBUG to add dev_vdbg() messages to the +ones already enabled by DEBUG. + Chapter 14: Allocating memory @@ -790,4 +805,5 @@ Kernel CodingStyle, by greg@kroah.com at OLS 2002: http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/ -- -Last updated on 2006-December-06. +Last updated on 2007-July-13. + diff --git a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl index fd2ef4d29b6..a0af560ed74 100644 --- a/Documentation/DocBook/kernel-api.tmpl +++ b/Documentation/DocBook/kernel-api.tmpl @@ -408,6 +408,10 @@ X!Edrivers/pnp/system.c !Edrivers/pnp/manager.c !Edrivers/pnp/support.c </sect1> + <sect1><title>Userspace IO devices</title> +!Edrivers/uio/uio.c +!Iinclude/linux/uio_driver.h + </sect1> </chapter> <chapter id="blkdev"> diff --git a/Documentation/DocBook/uio-howto.tmpl b/Documentation/DocBook/uio-howto.tmpl new file mode 100644 index 00000000000..e3bb29a8d8d --- /dev/null +++ b/Documentation/DocBook/uio-howto.tmpl @@ -0,0 +1,611 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" +"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd" []> + +<book id="index"> +<bookinfo> +<title>The Userspace I/O HOWTO</title> + +<author> + <firstname>Hans-Jürgen</firstname> + <surname>Koch</surname> + <authorblurb><para>Linux developer, Linutronix</para></authorblurb> + <affiliation> + <orgname> + <ulink url="http://www.linutronix.de">Linutronix</ulink> + </orgname> + + <address> + <email>hjk@linutronix.de</email> + </address> + </affiliation> +</author> + +<pubdate>2006-12-11</pubdate> + +<abstract> + <para>This HOWTO describes concept and usage of Linux kernel's + Userspace I/O system.</para> +</abstract> + +<revhistory> + <revision> + <revnumber>0.3</revnumber> + <date>2007-04-29</date> + <authorinitials>hjk</authorinitials> + <revremark>Added section about userspace drivers.</revremark> + </revision> + <revision> + <revnumber>0.2</revnumber> + <date>2007-02-13</date> + <authorinitials>hjk</authorinitials> + <revremark>Update after multiple mappings were added.</revremark> + </revision> + <revision> + <revnumber>0.1</revnumber> + <date>2006-12-11</date> + <authorinitials>hjk</authorinitials> + <revremark>First draft.</revremark> + </revision> +</revhistory> +</bookinfo> + +<chapter id="aboutthisdoc"> +<?dbhtml filename="about.html"?> +<title>About this document</title> + +<sect1 id="copyright"> +<?dbhtml filename="copyright.html"?> +<title>Copyright and License</title> +<para> + Copyright (c) 2006 by Hans-Jürgen Koch.</para> +<para> +This documentation is Free Software licensed under the terms of the +GPL version 2. +</para> +</sect1> + +<sect1 id="translations"> +<?dbhtml filename="translations.html"?> +<title>Translations</title> + +<para>If you know of any translations for this document, or you are +interested in translating it, please email me +<email>hjk@linutronix.de</email>. +</para> +</sect1> + +<sect1 id="preface"> +<title>Preface</title> + <para> + For many types of devices, creating a Linux kernel driver is + overkill. All that is really needed is some way to handle an + interrupt and provide access to the memory space of the + device. The logic of controlling the device does not + necessarily have to be within the kernel, as the device does + not need to take advantage of any of other resources that the + kernel provides. One such common class of devices that are + like this are for industrial I/O cards. + </para> + <para> + To address this situation, the userspace I/O system (UIO) was + designed. For typical industrial I/O cards, only a very small + kernel module is needed. The main part of the driver will run in + user space. This simplifies development and reduces the risk of + serious bugs within a kernel module. + </para> +</sect1> + +<sect1 id="thanks"> +<title>Acknowledgments</title> + <para>I'd like to thank Thomas Gleixner and Benedikt Spranger of + Linutronix, who have not only written most of the UIO code, but also + helped greatly writing this HOWTO by giving me all kinds of background + information.</para> +</sect1> + +<sect1 id="feedback"> +<title>Feedback</title> + <para>Find something wrong with this document? (Or perhaps something + right?) I would love to hear from you. Please email me at + <email>hjk@linutronix.de</email>.</para> +</sect1> +</chapter> + +<chapter id="about"> +<?dbhtml filename="about.html"?> +<title>About UIO</title> + +<para>If you use UIO for your card's driver, here's what you get:</para> + +<itemizedlist> +<listitem> + <para>only one small kernel module to write and maintain.</para> +</listitem> +<listitem> + <para>develop the main part of your driver in user space, + with all the tools and libraries you're used to.</para> +</listitem> +<listitem> + <para>bugs in your driver won't crash the kernel.</para> +</listitem> +<listitem> + <para>updates of your driver can take place without recompiling + the kernel.</para> +</listitem> +<listitem> + <para>if you need to keep some parts of your driver closed source, + you can do so without violating the GPL license on the kernel.</para> +</listitem> +</itemizedlist> + +<sect1 id="how_uio_works"> +<title>How UIO works</title> + <para> + Each UIO device is accessed through a device file and several + sysfs attribute files. The device file will be called + <filename>/dev/uio0</filename> for the first device, and + <filename>/dev/uio1</filename>, <filename>/dev/uio2</filename> + and so on for subsequent devices. + </para> + + <para><filename>/dev/uioX</filename> is used to access the + address space of the card. Just use + <function>mmap()</function> to access registers or RAM + locations of your card. + </para> + + <para> + Interrupts are handled by reading from + <filename>/dev/uioX</filename>. A blocking + <function>read()</function> from + <filename>/dev/uioX</filename> will return as soon as an + interrupt occurs. You can also use + <function>select()</function> on + <filename>/dev/uioX</filename> to wait for an interrupt. The + integer value read from <filename>/dev/uioX</filename> + represents the total interrupt count. You can use this number + to figure out if you missed some interrupts. + </para> + + <para> + To handle interrupts properly, your custom kernel module can + provide its own interrupt handler. It will automatically be + called by the built-in handler. + </para> + + <para> + For cards that don't generate interrupts but need to be + polled, there is the possibility to set up a timer that + triggers the interrupt handler at configurable time intervals. + See <filename>drivers/uio/uio_dummy.c</filename> for an + example of this technique. + </para> + + <para> + Each driver provides attributes that are used to read or write + variables. These attributes are accessible through sysfs + files. A custom kernel driver module can add its own + attributes to the device owned by the uio driver, but not added + to the UIO device itself at this time. This might change in the + future if it would be found to be useful. + </para> + + <para> + The following standard attributes are provided by the UIO + framework: + </para> +<itemizedlist> +<listitem> + <para> + <filename>name</filename>: The name of your device. It is + recommended to use the name of your kernel module for this. + </para> +</listitem> +<listitem> + <para> + <filename>version</filename>: A version string defined by your + driver. This allows the user space part of your driver to deal + with different versions of the kernel module. + </para> +</listitem> +<listitem> + <para> + <filename>event</filename>: The total number of interrupts + handled by the driver since the last time the device node was + read. + </para> +</listitem> +</itemizedlist> +<para> + These attributes appear under the + <filename>/sys/class/uio/uioX</filename> directory. Please + note that this directory might be a symlink, and not a real + directory. Any userspace code that accesses it must be able + to handle this. +</para> +<para> + Each UIO device can make one or more memory regions available for + memory mapping. This is necessary because some industrial I/O cards + require access to more than one PCI memory region in a driver. +</para> +<para> + Each mapping has its own directory in sysfs, the first mapping + appears as <filename>/sys/class/uio/uioX/maps/map0/</filename>. + Subsequent mappings create directories <filename>map1/</filename>, + <filename>map2/</filename>, and so on. These directories will only + appear if the size of the mapping is not 0. +</para> +<para> + Each <filename>mapX/</filename> directory contains two read-only files + that show start address and size of the memory: +</para> +<itemizedlist> +<listitem> + <para> + <filename>addr</filename>: The address of memory that can be mapped. + </para> +</listitem> +<listitem> + <para> + <filename>size</filename>: The size, in bytes, of the memory + pointed to by addr. + </para> +</listitem> +</itemizedlist> + +<para> + From userspace, the different mappings are distinguished by adjusting + the <varname>offset</varname> parameter of the + <function>mmap()</function> call. To map the memory of mapping N, you + have to use N times the page size as your offset: +</para> +<programlisting format="linespecific"> +offset = N * getpagesize(); +</programlisting> + +</sect1> +</chapter> + +<chapter id="using-uio_dummy" xreflabel="Using uio_dummy"> +<?dbhtml filename="using-uio_dummy.html"?> +<title>Using uio_dummy</title> + <para> + Well, there is no real use for uio_dummy. Its only purpose is + to test most parts of the UIO system (everything except + hardware interrupts), and to serve as an example for the + kernel module that you will have to write yourself. + </para> + +<sect1 id="what_uio_dummy_does"> +<title>What uio_dummy does</title> + <para> + The kernel module <filename>uio_dummy.ko</filename> creates a + device that uses a timer to generate periodic interrupts. The + interrupt handler does nothing but increment a counter. The + driver adds two custom attributes, <varname>count</varname> + and <varname>freq</varname>, that appear under + <filename>/sys/devices/platform/uio_dummy/</filename>. + </para> + + <para> + The attribute <varname>count</varname> can be read and + written. The associated file + <filename>/sys/devices/platform/uio_dummy/count</filename> + appears as a normal text file and contains the total number of + timer interrupts. If you look at it (e.g. using + <function>cat</function>), you'll notice it is slowly counting + up. + </para> + + <para> + The attribute <varname>freq</varname> can be read and written. + The content of + <filename>/sys/devices/platform/uio_dummy/freq</filename> + represents the number of system timer ticks between two timer + interrupts. The default value of <varname>freq</varname> is + the value of the kernel variable <varname>HZ</varname>, which + gives you an interval of one second. Lower values will + increase the frequency. Try the following: + </para> +<programlisting format="linespecific"> +cd /sys/devices/platform/uio_dummy/ +echo 100 > freq +</programlisting> + <para> + Use <function>cat count</function> to see how the interrupt + frequency changes. + </para> +</sect1> +</chapter> + +<chapter id="custom_kernel_module" xreflabel="Writing your own kernel module"> +<?dbhtml filename="custom_kernel_module.html"?> +<title>Writing your own kernel module</title> + <para> + Please have a look at <filename>uio_dummy.c</filename> as an + example. The following paragraphs explain the different + sections of this file. + </para> + +<sect1 id="uio_info"> +<title>struct uio_info</title> + <para> + This structure tells the framework the details of your driver, + Some of the members are required, others are optional. + </para> + +<itemizedlist> +<listitem><para> +<varname>char *name</varname>: Required. The name of your driver as +it will appear in sysfs. I recommend using the name of your module for this. +</para></listitem> + +<listitem><para> +<varname>char *version</varname>: Required. This string appears in +<filename>/sys/class/uio/uioX/version</filename>. +</para></listitem> + +<listitem><para> +<varname>struct uio_mem mem[ MAX_UIO_MAPS ]</varname>: Required if you +have memory that can be mapped with <function>mmap()</function>. For each +mapping you need to fill one of the <varname>uio_mem</varname> structures. +See the description below for details. +</para></listitem> + +<listitem><para> +<varname>long irq</varname>: Required. If your hardware generates an +interrupt, it's your modules task to determine the irq number during +initialization. If you don't have a hardware generated interrupt but +want to trigger the interrupt handler in some other way, set +<varname>irq</varname> to <varname>UIO_IRQ_CUSTOM</varname>. The +uio_dummy module does this as it triggers the event mechanism in a timer +routine. If you had no interrupt at all, you could set +<varname>irq</varname> to <varname>UIO_IRQ_NONE</varname>, though this +rarely makes sense. +</para></listitem> + +<listitem><para> +<varname>unsigned long irq_flags</varname>: Required if you've set +<varname>irq</varname> to a hardware interrupt number. The flags given +here will be used in the call to <function>request_irq()</function>. +</para></listitem> + +<listitem><para> +<varname>int (*mmap)(struct uio_info *info, struct vm_area_struct +*vma)</varname>: Optional. If you need a special +<function>mmap()</function> function, you can set it here. If this +pointer is not NULL, your <function>mmap()</function> will be called +instead of the built-in one. +</para></listitem> + +<listitem><para> +<varname>int (*open)(struct uio_info *info, struct inode *inode) +</varname>: Optional. You might want to have your own +<function>open()</function>, e.g. to enable interrupts only when your +device is actually used. +</para></listitem> + +<listitem><para> +<varname>int (*release)(struct uio_info *info, struct inode *inode) +</varname>: Optional. If you define your own +<function>open()</function>, you will probably also want a custom +<function>release()</function> function. +</para></listitem> +</itemizedlist> + +<para> +Usually, your device will have one or more memory regions that can be mapped +to user space. For each region, you have to set up a +<varname>struct uio_mem</varname> in the <varname>mem[]</varname> array. +Here's a description of the fields of <varname>struct uio_mem</varname>: +</para> + +<itemizedlist> +<listitem><para> +<varname>int memtype</varname>: Required if the mapping is used. Set this to +<varname>UIO_MEM_PHYS</varname> if you you have physical memory on your +card to be mapped. Use <varname>UIO_MEM_LOGICAL</varname> for logical +memory (e.g. allocated with <function>kmalloc()</function>). There's also +<varname>UIO_MEM_VIRTUAL</varname> for virtual memory. +</para></listitem> + +<listitem><para> +<varname>unsigned long addr</varname>: Required if the mapping is used. +Fill in the address of your memory block. This address is the one that +appears in sysfs. +</para></listitem> + +<listitem><para> +<varname>unsigned long size</varname>: Fill in the size of the +memory block that <varname>addr</varname> points to. If <varname>size</varname> +is zero, the mapping is considered unused. Note that you +<emphasis>must</emphasis> initialize <varname>size</varname> with zero for +all unused mappings. +</para></listitem> + +<listitem><para> +<varname>void *internal_addr</varname>: If you have to access this memory +region from within your kernel module, you will want to map it internally by +using something like <function>ioremap()</function>. Addresses +returned by this function cannot be mapped to user space, so you must not +store it in <varname>addr</varname>. Use <varname>internal_addr</varname> +instead to remember such an address. +</para></listitem> +</itemizedlist> + +<para> +Please do not touch the <varname>kobj</varname> element of +<varname>struct uio_mem</varname>! It is used by the UIO framework +to set up sysfs files for this mapping. Simply leave it alone. +</para> +</sect1> + +<sect1 id="adding_irq_handler"> +<title>Adding an interrupt handler</title> + <para> + What you need to do in your interrupt handler depends on your + hardware and on how you want to handle it. You should try to + keep the amount of code in your kernel interrupt handler low. + If your hardware requires no action that you + <emphasis>have</emphasis> to perform after each interrupt, + then your handler can be empty.</para> <para>If, on the other + hand, your hardware <emphasis>needs</emphasis> some action to + be performed after each interrupt, then you + <emphasis>must</emphasis> do it in your kernel module. Note + that you cannot rely on the userspace part of your driver. Your + userspace program can terminate at any time, possibly leaving + your hardware in a state where proper interrupt handling is + still required. + </para> + + <para> + There might also be applications where you want to read data + from your hardware at each interrupt and buffer it in a piece + of kernel memory you've allocated for that purpose. With this + technique you could avoid loss of data if your userspace + program misses an interrupt. + </para> + + <para> + A note on shared interrupts: Your driver should support + interrupt sharing whenever this is possible. It is possible if + and only if your driver can detect whether your hardware has + triggered the interrupt or not. This is usually done by looking + at an interrupt status register. If your driver sees that the + IRQ bit is actually set, it will perform its actions, and the + handler returns IRQ_HANDLED. If the driver detects that it was + not your hardware that caused the interrupt, it will do nothing + and return IRQ_NONE, allowing the kernel to call the next + possible interrupt handler. + </para> + + <para> + If you decide not to support shared interrupts, your card + won't work in computers with no free interrupts. As this + frequently happens on the PC platform, you can save yourself a + lot of trouble by supporting interrupt sharing. + </para> +</sect1> + +</chapter> + +<chapter id="userspace_driver" xreflabel="Writing a driver in user space"> +<?dbhtml filename="userspace_driver.html"?> +<title>Writing a driver in userspace</title> + <para> + Once you have a working kernel module for your hardware, you can + write the userspace part of your driver. You don't need any special + libraries, your driver can be written in any reasonable language, + you can use floating point numbers and so on. In short, you can + use all the tools and libraries you'd normally use for writing a + userspace application. + </para> + +<sect1 id="getting_uio_information"> +<title>Getting information about your UIO device</title> + <para> + Information about all UIO devices is available in sysfs. The + first thing you should do in your driver is check + <varname>name</varname> and <varname>version</varname> to + make sure your talking to the right device and that its kernel + driver has the version you expect. + </para> + <para> + You should also make sure that the memory mapping you need + exists and has the size you expect. + </para> + <para> + There is a tool called <varname>lsuio</varname> that lists + UIO devices and their attributes. It is available here: + </para> + <para> + <ulink url="http://www.osadl.org/projects/downloads/UIO/user/"> + http://www.osadl.org/projects/downloads/UIO/user/</ulink> + </para> + <para> + With <varname>lsuio</varname> you can quickly check if your + kernel module is loaded and which attributes it exports. + Have a look at the manpage for details. + </para> + <para> + The source code of <varname>lsuio</varname> can serve as an + example for getting information about an UIO device. + The file <filename>uio_helper.c</filename> contains a lot of + functions you could use in your userspace driver code. + </para> +</sect1> + +<sect1 id="mmap_device_memory"> +<title>mmap() device memory</title> + <para> + After you made sure you've got the right device with the + memory mappings you need, all you have to do is to call + <function>mmap()</function> to map the device's memory + to userspace. + </para> + <para> + The parameter <varname>offset</varname> of the + <function>mmap()</function> call has a special meaning + for UIO devices: It is used to select which mapping of + your device you want to map. To map the memory of + mapping N, you have to use N times the page size as + your offset: + </para> +<programlisting format="linespecific"> + offset = N * getpagesize(); +</programlisting> + <para> + N starts from zero, so if you've got only one memory + range to map, set <varname>offset = 0</varname>. + A drawback of this technique is that memory is always + mapped beginning with its start address. + </para> +</sect1> + +<sect1 id="wait_for_interrupts"> +<title>Waiting for interrupts</title> + <para> + After you successfully mapped your devices memory, you + can access it like an ordinary array. Usually, you will + perform some initialization. After that, your hardware + starts working and will generate an interrupt as soon + as it's finished, has some data available, or needs your + attention because an error occured. + </para> + <para> + <filename>/dev/uioX</filename> is a read-only file. A + <function>read()</function> will always block until an + interrupt occurs. There is only one legal value for the + <varname>count</varname> parameter of + <function>read()</function>, and that is the size of a + signed 32 bit integer (4). Any other value for + <varname>count</varname> causes <function>read()</function> + to fail. The signed 32 bit integer read is the interrupt + count of your device. If the value is one more than the value + you read the last time, everything is OK. If the difference + is greater than one, you missed interrupts. + </para> + <para> + You can also use <function>select()</function> on + <filename>/dev/uioX</filename>. + </para> +</sect1> + +</chapter> + +<appendix id="app1"> +<title>Further information</title> +<itemizedlist> + <listitem><para> + <ulink url="http://www.osadl.org"> + OSADL homepage.</ulink> + </para></listitem> + <listitem><para> + <ulink url="http://www.linutronix.de"> + Linutronix homepage.</ulink> + </para></listitem> +</itemizedlist> +</appendix> + +</book> diff --git a/Documentation/HOWTO b/Documentation/HOWTO index 98e2701c746..f8cc3f8ed15 100644 --- a/Documentation/HOWTO +++ b/Documentation/HOWTO @@ -249,6 +249,9 @@ process is as follows: release a new -rc kernel every week. - Process continues until the kernel is considered "ready", the process should last around 6 weeks. + - A list of known regressions present in each -rc release is + tracked at the following URI: + http://kernelnewbies.org/known_regressions It is worth mentioning what Andrew Morton wrote on the linux-kernel mailing list about kernel releases: diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index 6c8d8f27db3..8569072fa38 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt @@ -207,7 +207,7 @@ responsibility. This is usually non-issue because bus ops and resource allocations already do the job. For an example of single-instance devres type, read pcim_iomap_table() -in lib/iomap.c. +in lib/devres.c. All devres interface functions can be called without context if the right gfp mask is given. diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index d05e6243b4d..66c8b4b165c 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -26,9 +26,7 @@ Who: Hans Verkuil <hverkuil@xs4all.nl> and --------------------------- -What: /sys/devices/.../power/state - dev->power.power_state - dpm_runtime_{suspend,resume)() +What: dev->power.power_state When: July 2007 Why: Broken design for runtime control over driver power states, confusing driver-internal runtime power management with: mechanisms to support @@ -310,3 +308,13 @@ Why: The arch/powerpc tree is the merged architecture for ppc32 and ppc64 Who: linuxppc-dev@ozlabs.org --------------------------- + +What: mthca driver's MSI support +When: January 2008 +Files: drivers/infiniband/hw/mthca/*.[ch] +Why: All mthca hardware also supports MSI-X, which provides + strictly more functionality than MSI. So there is no point in + having both MSI-X and MSI support in the driver. +Who: Roland Dreier <rolandd@cisco.com> + +--------------------------- diff --git a/Documentation/ja_JP/HOWTO b/Documentation/ja_JP/HOWTO new file mode 100644 index 00000000000..b2446a09087 --- /dev/null +++ b/Documentation/ja_JP/HOWTO @@ -0,0 +1,650 @@ +NOTE: +This is Japanese translated version of "Documentation/HOWTO". +This one is maintained by Tsugikazu Shibata <tshibata@ab.jp.nec.com> +and JF Project team <www.linux.or.jp/JF>. +If you find difference with original file or problem in translation, +please contact maintainer of this file or JF project. + +Please also note that purpose of this file is easier to read for non +English natives and not to be intended to fork. So, if you have any +comments or updates of this file, please try to update Original(English) +file at first. + +Last Updated: 2007/06/04 +================================== +これは、 +linux-2.6.21/Documentation/HOWTO +の和訳です。 + +翻訳団体: JF プロジェクト < http://www.linux.or.jp/JF/ > +翻訳日: 2007/06/04 +翻訳者: Tsugikazu Shibata <tshibata at ab dot jp dot nec dot com> +校正者: 松倉さん <nbh--mats at nifty dot com> + 小林 雅典さん (Masanori Kobayasi) <zap03216 at nifty dot ne dot jp> + 武井伸光さん、<takei at webmasters dot gr dot jp> + かねこさん (Seiji Kaneko) <skaneko at a2 dot mbn dot or dot jp> + 野口さん (Kenji Noguchi) <tokyo246 at gmail dot com> + 河内さん (Takayoshi Kochi) <t-kochi at bq dot jp dot nec dot com> + 岩本さん (iwamoto) <iwamoto.kn at ncos dot nec dot co dot jp> +================================== + +Linux カーネル開発のやり方 +------------------------------- + +これは上のトピック( Linux カーネル開発のやり方)の重要な事柄を網羅した +ドキュメントです。ここには Linux カーネル開発者になるための方法と +Linux カーネル開発コミュニティと共に活動するやり方を学ぶ方法が含まれて +います。カーネルプログラミングに関する技術的な項目に関することは何も含 +めないようにしていますが、カーネル開発者となるための正しい方向に向かう +手助けになります。 + +もし、このドキュメントのどこかが古くなっていた場合には、このドキュメン +トの最後にリストしたメンテナーにパッチを送ってください。 + +はじめに +--------- + +あなたは Linux カーネルの開発者になる方法を学びたいのでしょうか? そ +れともあなたは上司から「このデバイスの Linux ドライバを書くように」と +言われているのでしょうか? +この文書の目的は、あなたが踏むべき手順と、コミュニティと一緒にうまく働 +くヒントを書き下すことで、あなたが知るべき全てのことを教えることです。 +また、このコミュニティがなぜ今うまくまわっているのかという理由の一部も +説明しようと試みています。 + +カーネルは 少量のアーキテクチャ依存部分がアセンブリ言語で書かれている +以外は大部分は C 言語で書かれています。C言語をよく理解していることはカー +ネル開発者には必要です。アーキテクチャ向けの低レベル部分の開発をするの +でなければ、(どんなアーキテクチャでも)アセンブリ(訳注: 言語)は必要あり +ません。以下の本は、C 言語の十分な知識や何年もの経験に取って代わるもの +ではありませんが、少なくともリファレンスとしてはいい本です。 + - "The C Programming Language" by Kernighan and Ritchie [Prentice Hall] + -『プログラミング言語C第2版』(B.W. カーニハン/D.M. リッチー著 石田晴久訳) [共立出版] + - "Practical C Programming" by Steve Oualline [O'Reilly] + - 『C実践プログラミング第3版』(Steve Oualline著 望月康司監訳 谷口功訳) [オライリージャパン] + - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] + - 『新・詳説 C 言語 H&S リファレンス』 + (サミュエル P ハービソン/ガイ L スティール共著 斉藤 信男監訳)[ソフトバンク] + +カーネルは GNU C と GNU ツールチェインを使って書かれています。カーネル +は ISO C89 仕様に準拠して書く一方で、標準には無い言語拡張を多く使って +います。カーネルは標準 C ライブラリとは関係がないといった、C 言語フリー +スタンディング環境です。そのため、C の標準で使えないものもあります。任 +意の long long の除算や浮動小数点は使えません。 +ときどき、カーネルがツールチェインや C 言語拡張に置いている前提がどう +なっているのかわかりにくいことがあり、また、残念なことに決定的なリファ +レンスは存在しません。情報を得るには、gcc の info ページ( info gcc )を +みてください。 + +あなたは既存の開発コミュニティと一緒に作業する方法を学ぼうとしているこ +とに留意してください。そのコミュニティは、コーディング、スタイル、 +開発手順について高度な標準を持つ、多様な人の集まりです。 +地理的に分散した大規模なチームに対してもっともうまくいくとわかったこと +をベースにしながら、これらの標準は長い時間をかけて築かれてきました。 +これらはきちんと文書化されていますから、事前にこれらの標準についてでき +るだけたくさん学んでください。また皆があなたやあなたの会社のやり方に合わ +せてくれると思わないでください。 + +法的問題 +------------ + +Linux カーネルのソースコードは GPL ライセンスの下でリリースされていま +す。ライセンスの詳細については、ソースツリーのメインディレクトリに存在 +する、COPYING のファイルをみてください。もしライセンスについてさらに質 +問があれば、Linux Kernel メーリングリストに質問するのではなく、どうぞ +法律家に相談してください。メーリングリストの人達は法律家ではなく、法的 +問題については彼らの声明はあてにするべきではありません。 + +GPL に関する共通の質問や回答については、以下を参照してください。 + http://www.gnu.org/licenses/gpl-faq.html + +ドキュメント +------------ + +Linux カーネルソースツリーは幅広い範囲のドキュメントを含んでおり、それ +らはカーネルコミュニティと会話する方法を学ぶのに非常に貴重なものです。 +新しい機能がカーネルに追加される場合、その機能の使い方について説明した +新しいドキュメントファイルも追加することを勧めます。 +カーネルの変更が、カーネルがユーザ空間に公開しているインターフェイスの +変更を引き起こす場合、その変更を説明するマニュアルページのパッチや情報 +をマニュアルページのメンテナ mtk-manpages@gmx.net に送ることを勧めます。 + +以下はカーネルソースツリーに含まれている読んでおくべきファイルの一覧で +す- + + README + このファイルは Linuxカーネルの簡単な背景とカーネルを設定(訳注 + configure )し、生成(訳注 build )するために必要なことは何かが書かれ + ています。カーネルに関して初めての人はここからスタートするとよいで + しょう。 + + Documentation/Changes + このファイルはカーネルをうまく生成(訳注 build )し、走らせるのに最 + 小限のレベルで必要な数々のソフトウェアパッケージの一覧を示してい + ます。 + + Documentation/CodingStyle + これは Linux カーネルのコーディングスタイルと背景にある理由を記述 + しています。全ての新しいコードはこのドキュメントにあるガイドライン + に従っていることを期待されています。大部分のメンテナーはこれらのルー + ルに従っているものだけを受け付け、多くの人は正しいスタイルのコード + だけをレビューします。 + + Documentation/SubmittingPatches + Documentation/SubmittingDrivers + これらのファイルには、どうやってうまくパッチを作って投稿するかに + ついて非常に詳しく書かれており、以下を含みます(これだけに限らない + けれども) + - Email に含むこと + - Email の形式 + - だれに送るか + これらのルールに従えばうまくいくことを保証することではありません + が (すべてのパッチは内容とスタイルについて精査を受けるので)、 + ルールに従わなければ間違いなくうまくいかないでしょう。 + この他にパッチを作る方法についてのよくできた記述は- + + "The Perfect Patch" + http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + "Linux kernel patch submission format" + http://linux.yyz.us/patch-format.html + + Documentation/stable_api_nonsense.txt + このファイルはカーネルの中に不変のAPIを持たないことにした意識的な + 決断の背景にある理由について書かれています。以下のようなことを含 + んでいます- + - サブシステムとの間に層を作ること(コンパチビリティのため?) + - オペレーティングシステム間のドライバの移植性 + - カーネルソースツリーの素早い変更を遅らせる(もしくは素早い変更 + を妨げる) + このドキュメントは Linux 開発の思想を理解するのに非常に重要です。 + そして、他のOSでの開発者が Linux に移る時にとても重要です。 + + Documentation/SecurityBugs + もし Linux カーネルでセキュリティ問題を発見したように思ったら、こ + のドキュメントのステップに従ってカーネル開発者に連絡し、問題解決を + 支援してください。 + + Documentation/ManagementStyle + このドキュメントは Linux カーネルのメンテナー達がどう行動するか、 + 彼らの手法の背景にある共有されている精神について記述しています。こ + れはカーネル開発の初心者なら(もしくは、単に興味があるだけの人でも) + 重要です。なぜならこのドキュメントは、カーネルメンテナー達の独特な + 行動についての多くの誤解や混乱を解消するからです。 + + Documentation/stable_kernel_rules.txt + このファイルはどのように stable カーネルのリリースが行われるかのルー + ルが記述されています。そしてこれらのリリースの中のどこかで変更を取 + り入れてもらいたい場合に何をすればいいかが示されています。 + + Documentation/kernel-docs.txt + カーネル開発に付随する外部ドキュメントのリストです。もしあなたが + 探しているものがカーネル内のドキュメントでみつからなかった場合、 + このリストをあたってみてください。 + + Documentation/applying-patches.txt + パッチとはなにか、パッチをどうやって様々なカーネルの開発ブランチに + 適用するのかについて正確に記述した良い入門書です。 + +カーネルはソースコードから自動的に生成可能な多数のドキュメントを自分自 +身でもっています。これにはカーネル内 API のすべての記述や、どう正しく +ロックをかけるかの規則が含まれます。このドキュメントは +Documentation/DocBook/ ディレクトリに作られ、以下のように + make pdfdocs + make psdocs + make htmldocs + make mandocs +コマンドを実行するとメインカーネルのソースディレクトリから +それぞれ、PDF, Postscript, HTML, man page の形式で生成されます。 + +カーネル開発者になるには +--------------------------- + +もしあなたが、Linux カーネル開発について何も知らないならば、 +KernelNewbies プロジェクトを見るべきです + http://kernelnewbies.org + +このサイトには役に立つメーリングリストがあり、基本的なカーネル開発に関 +するほとんどどんな種類の質問もできます (既に回答されているようなことを +聞く前にまずはアーカイブを調べてください)。 +またここには、リアルタイムで質問を聞くことができる IRC チャネルや、Linux +カーネルの開発に関して学ぶのに便利なたくさんの役に立つドキュメントがあ +ります。 + +web サイトには、コードの構成、サブシステム、現在存在するプロジェクト(ツ +リーにあるもの無いものの両方)の基本的な管理情報があります。 +ここには、また、カーネルのコンパイルのやり方やパッチの当て方などの間接 +的な基本情報も記述されています。 + +あなたがどこからスタートしてよいかわからないが、Linux カーネル開発コミュ +ニティに参加して何かすることをさがしている場合には、Linux kernel +Janitor's プロジェクトにいけばよいでしょう - + http://janitor.kernelnewbies.org/ +ここはそのようなスタートをするのにうってつけの場所です。ここには、 +Linux カーネルソースツリーの中に含まれる、きれいにし、修正しなければな +らない、単純な問題のリストが記述されています。このプロジェクトに関わる +開発者と一緒に作業することで、あなたのパッチを Linuxカーネルツリーに入 +れるための基礎を学ぶことができ、そしてもしあなたがまだアイディアを持っ +ていない場合には、次にやる仕事の方向性が見えてくるかもしれません。 + +もしあなたが、すでにひとまとまりコードを書いていて、カーネルツリーに入 +れたいと思っていたり、それに関する適切な支援を求めたい場合、カーネル +メンターズプロジェクトはそのような皆さんを助けるためにできました。 +ここにはメーリングリストがあり、以下から参照できます + http://selenic.com/mailman/listinfo/kernel-mentors + +実際に Linux カーネルのコードについて修正を加える前に、どうやってその +コードが動作するのかを理解することが必要です。そのためには、特別なツー +ルの助けを借りてでも、それを直接よく読むことが最良の方法です(ほとんど +のトリッキーな部分は十分にコメントしてありますから)。そういうツールで +特におすすめなのは、Linux クロスリファレンスプロジェクトです。これは、 +自己参照方式で、索引がついた web 形式で、ソースコードを参照することが +できます。この最新の素晴しいカーネルコードのリポジトリは以下で見つかり +ます- + http://sosdg.org/~coywolf/lxr/ + +開発プロセス +----------------------- + +Linux カーネルの開発プロセスは現在幾つかの異なるメインカーネル「ブラン +チ」と多数のサブシステム毎のカーネルブランチから構成されます。 +これらのブランチとは- + - メインの 2.6.x カーネルツリー + - 2.6.x.y -stable カーネルツリー + - 2.6.x -git カーネルパッチ + - 2.6.x -mm カーネルパッチ + - サブシステム毎のカーネルツリーとパッチ + +2.6.x カーネルツリー +----------------- + +2.6.x カーネルは Linus Torvalds によってメンテナンスされ、kernel.org +の pub/linux/kernel/v2.6/ ディレクトリに存在します。この開発プロセスは +以下のとおり- + + - 新しいカーネルがリリースされた直後に、2週間の特別期間が設けられ、 + この期間中に、メンテナー達は Linus に大きな差分を送ることができま + す。このような差分は通常 -mm カーネルに数週間含まれてきたパッチで + す。 大きな変更は git(カーネルのソース管理ツール、詳細は + http://git.or.cz/ 参照) を使って送るのが好ましいやり方ですが、パッ + チファイルの形式のまま送るのでも十分です。 + + - 2週間後、-rc1 カーネルがリリースされ、この後にはカーネル全体の安定 + 性に影響をあたえるような新機能は含まない類のパッチしか取り込むこと + はできません。新しいドライバ(もしくはファイルシステム)のパッチは + -rc1 の後で受け付けられることもあることを覚えておいてください。な + ぜなら、変更が独立していて、追加されたコードの外の領域に影響を与え + ない限り、退行のリスクは無いからです。-rc1 がリリースされた後、 + Linus へパッチを送付するのに git を使うこともできますが、パッチは + レビューのために、パブリックなメーリングリストへも同時に送る必要が + あります。 + + - 新しい -rc は Linus が、最新の git ツリーがテスト目的であれば十分 + に安定した状態にあると判断したときにリリースされます。目標は毎週新 + しい -rc カーネルをリリースすることです。 + + - このプロセスはカーネルが 「準備ができた」と考えられるまで継続しま + す。このプロセスはだいたい 6週間継続します。 + +Andrew Morton が Linux-kernel メーリングリストにカーネルリリースについ +て書いたことをここで言っておくことは価値があります- + 「カーネルがいつリリースされるかは誰も知りません。なぜなら、これは現 + 実に認識されたバグの状況によりリリースされるのであり、前もって決めら + れた計画によってリリースされるものではないからです。」 + +2.6.x.y -stable カーネルツリー +--------------------------- + +バージョンに4つ目の数字がついたカーネルは -stable カーネルです。これに +は、2.6.x カーネルで見つかったセキュリティ問題や重大な後戻りに対する比 +較的小さい重要な修正が含まれます。 + +これは、開発/実験的バージョンのテストに協力することに興味が無く、 +最新の安定したカーネルを使いたいユーザに推奨するブランチです。 + +もし、2.6.x.y カーネルが存在しない場合には、番号が一番大きい 2.6.x +が最新の安定版カーネルです。 + +2.6.x.y は "stable" チーム <stable@kernel.org> でメンテされており、だ +いたい隔週でリリースされています。 + +カーネルツリーに入っている、Documentation/stable_kernel_rules.txt ファ +イルにはどのような種類の変更が -stable ツリーに受け入れ可能か、またリ +リースプロセスがどう動くかが記述されています。 + +2.6.x -git パッチ +------------------ + +git リポジトリで管理されているLinus のカーネルツリーの毎日のスナップ +ショットがあります。(だから -git という名前がついています)。これらのパッ +チはおおむね毎日リリースされており、Linus のツリーの現状を表します。こ +れは -rc カーネルと比べて、パッチが大丈夫かどうかも確認しないで自動的 +に生成されるので、より実験的です。 + +2.6.x -mm カーネルパッチ +------------------------ + +Andrew Morton によってリリースされる実験的なカーネルパッチ群です。 +Andrew は個別のサブシステムカーネルツリーとパッチを全て集めてきて +linux-kernel メーリングリストで収集された多数のパッチと同時に一つにま +とめます。 +このツリーは新機能とパッチが検証される場となります。ある期間の間パッチ +が -mm に入って価値を証明されたら、Andrew やサブシステムメンテナが、メ +インラインへ入れるように Linus にプッシュします。 + +メインカーネルツリーに含めるために Linus に送る前に、すべての新しいパッ +チが -mm ツリーでテストされることが強く推奨されます。 + +これらのカーネルは安定して動作すべきシステムとして使うのには適切ではあ +りませんし、カーネルブランチの中でももっとも動作にリスクが高いものです。 + +もしあなたが、カーネル開発プロセスの支援をしたいと思っているのであれば、 +どうぞこれらのカーネルリリースをテストに使ってみて、そしてもし問題があ +れば、またもし全てが正しく動作したとしても、linux-kernel メーリングリ +ストにフィードバックを提供してください。 + +すべての他の実験的パッチに加えて、これらのカーネルは通常リリース時点で +メインラインの -git カーネルに含まれる全ての変更も含んでいます。 + +-mm カーネルは決まったスケジュールではリリースされません、しかし通常幾 +つかの -mm カーネル (1 から 3 が普通)が各-rc カーネルの間にリリースさ +れます。 + +サブシステム毎のカーネルツリーとパッチ +------------------------------------------- + +カーネルの様々な領域で何が起きているかを見られるようにするため、多くの +カーネルサブシステム開発者は彼らの開発ツリーを公開しています。これらの +ツリーは説明したように -mm カーネルリリースに入れ込まれます。 + +以下はさまざまなカーネルツリーの中のいくつかのリスト- + + git ツリー- + - Kbuild の開発ツリー、Sam Ravnborg <sam@ravnborg.org> + kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git + + - ACPI の開発ツリー、 Len Brown <len.brown@intel.com> + kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git + + - Block の開発ツリー、Jens Axboe <axboe@suse.de> + kernel.org:/pub/scm/linux/kernel/git/axboe/linux-2.6-block.git + + - DRM の開発ツリー、Dave Airlie <airlied@linux.ie> + kernel.org:/pub/scm/linux/kernel/git/airlied/drm-2.6.git + + - ia64 の開発ツリー、Tony Luck <tony.luck@intel.com> + kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git + + - ieee1394 の開発ツリー、Jody McIntyre <scjody@modernduck.com> + kernel.org:/pub/scm/linux/kernel/git/scjody/ieee1394.git + + - infiniband, Roland Dreier <rolandd@cisco.com> + kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git + + - libata, Jeff Garzik <jgarzik@pobox.com> + kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git + + - ネットワークドライバ, Jeff Garzik <jgarzik@pobox.com> + kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git + + - pcmcia, Dominik Brodowski <linux@dominikbrodowski.net> + kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git + + - SCSI, James Bottomley <James.Bottomley@SteelEye.com> + kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git + + その他の git カーネルツリーは http://kernel.org/git に一覧表がありま + す。 + + quilt ツリー- + - USB, PCI ドライバコアと I2C, Greg Kroah-Hartman <gregkh@suse.de> + kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ + +バグレポート +------------- + +bugzilla.kernel.org は Linux カーネル開発者がカーネルのバグを追跡する +場所です。ユーザは見つけたバグの全てをこのツールで報告すべきです。 +どう kernel bugzilla を使うかの詳細は、以下を参照してください- + http://test.kernel.org/bugzilla/faq.html + +メインカーネルソースディレクトリにあるファイル REPORTING-BUGS はカーネ +ルバグらしいものについてどうレポートするかの良いテンプレートであり、問 +題の追跡を助けるためにカーネル開発者にとってどんな情報が必要なのかの詳 +細が書かれています。 + +メーリングリスト +------------- + +上のいくつかのドキュメントで述べていますが、コアカーネル開発者の大部分 +は Linux kernel メーリングリストに参加しています。このリストの登録/脱 +退の方法については以下を参照してください- + http://vger.kernel.org/vger-lists.html#linux-kernel + +このメーリングリストのアーカイブは web 上の多数の場所に存在します。こ +れらのアーカイブを探すにはサーチエンジンを使いましょう。例えば- + http://dir.gmane.org/gmane.linux.kernel + +リストに投稿する前にすでにその話題がアーカイブに存在するかどうかを検索 +することを是非やってください。多数の事がすでに詳細に渡って議論されて +おり、アーカイブにのみ記録されています。 + +大部分のカーネルサブシステムも自分の個別の開発を実施するメーリングリス +トを持っています。個々のグループがどんなリストを持っているかは、 +MAINTAINERS ファイルにリストがありますので参照してください。 + +多くのリストは kernel.org でホストされています。これらの情報は以下にあ +ります- + http://vger.kernel.org/vger-lists.html + +メーリングリストを使う場合、良い行動習慣に従うようにしましょう。 +少し安っぽいが、以下の URL は上のリスト(や他のリスト)で会話する場合の +シンプルなガイドラインを示しています- + http://www.albion.com/netiquette/ + +もし複数の人があなたのメールに返事をした場合、CC: で受ける人のリストは +だいぶ多くなるでしょう。良い理由がない場合、CC: リストから誰かを削除を +しないように、また、メーリングリストのアドレスだけにリプライすることの +ないようにしましょう。1つは送信者から、もう1つはリストからのように、メー +ルを2回受けることになってもそれに慣れ、しゃれたメールヘッダーを追加し +てこの状態を変えようとしないように。人々はそのようなことは好みません。 + +今までのメールでのやりとりとその間のあなたの発言はそのまま残し、 +"John Kernlehacker wrote ...:" の行をあなたのリプライの先頭行にして、 +メールの先頭でなく、各引用行の間にあなたの言いたいことを追加するべきで +す。 + +もしパッチをメールに付ける場合は、Documentaion/SubmittingPatches に提 +示されているように、それは プレーンな可読テキストにすることを忘れない +ようにしましょう。カーネル開発者は 添付や圧縮したパッチを扱いたがりま +せん- +彼らはあなたのパッチの行毎にコメントを入れたいので、そのためにはそうす +るしかありません。あなたのメールプログラムが空白やタブを圧縮しないよう +に確認した方がいいです。最初の良いテストとしては、自分にメールを送って +みて、そのパッチを自分で当ててみることです。もしそれがうまく行かないな +ら、あなたのメールプログラムを直してもらうか、正しく動くように変えるべ +きです。 + +とりわけ、他の登録者に対する尊敬を表すようにすることを覚えておいてくだ +さい。 + +コミュニティと共に働くこと +-------------------------- + +カーネルコミュニティのゴールは可能なかぎり最高のカーネルを提供すること +です。あなたがパッチを受け入れてもらうために投稿した場合、それは、技術 +的メリットだけがレビューされます。その際、あなたは何を予想すべきでしょ +うか? + - 批判 + - コメント + - 変更の要求 + - パッチの正当性の証明要求 + - 沈黙 + +思い出してください、ここはあなたのパッチをカーネルに入れる話です。あ +なたは、あなたのパッチに対する批判とコメントを受け入れるべきで、それら +を技術的レベルで評価して、パッチを再作成するか、なぜそれらの変更をすべ +きでないかを明確で簡潔な理由の説明を提供してください。 +もし、あなたのパッチに何も反応がない場合、たまにはメールの山に埋もれて +見逃され、あなたの投稿が忘れられてしまうこともあるので、数日待って再度 +投稿してください。 + +あなたがやるべきでないものは? + - 質問なしにあなたのパッチが受け入れられると想像すること + - 守りに入ること + - コメントを無視すること + - 要求された変更を何もしないでパッチを出し直すこと + +可能な限り最高の技術的解決を求めているコミュニティでは、パッチがどのく +らい有益なのかについては常に異なる意見があります。あなたは協調的である +べきですし、また、あなたのアイディアをカーネルに対してうまく合わせるよ +うにすることが望まれています。もしくは、最低限あなたのアイディアがそれ +だけの価値があるとすすんで証明するようにしなければなりません。 +正しい解決に向かって進もうという意志がある限り、間違うことがあっても許 +容されることを忘れないでください。 + +あなたの最初のパッチに単に 1ダースもの修正を求めるリストの返答になるこ +とも普通のことです。これはあなたのパッチが受け入れられないということで +は *ありません*、そしてあなた自身に反対することを意味するのでも *ありま +せん*。単に自分のパッチに対して指摘された問題を全て修正して再送すれば +いいのです。 + +カーネルコミュニティと企業組織のちがい +----------------------------------------------------------------- + +カーネルコミュニティは大部分の伝統的な会社の開発環境とは異ったやり方で +動いています。以下は問題を避けるためにできるとよいことののリストです- + + あなたの提案する変更について言うときのうまい言い方: + + - "これは複数の問題を解決します" + - "これは2000行のコードを削除します" + - "以下のパッチは、私が言おうとしていることを説明するものです" + - "私はこれを5つの異なるアーキテクチャでテストしたのですが..." + - "以下は一連の小さなパッチ群ですが..." + - "これは典型的なマシンでの性能を向上させます.." + + やめた方がいい悪い言い方: + + - このやり方で AIX/ptx/Solaris ではできたので、できるはずだ + - 私はこれを20年もの間やってきた、だから + - これは、私の会社が金儲けをするために必要だ + - これは我々のエンタープライズ向け商品ラインのためである + - これは 私が自分のアイディアを記述した、1000ページの設計資料である + - 私はこれについて、6ケ月作業している。 + - 以下は ... に関する5000行のパッチです + - 私は現在のぐちゃぐちゃを全部書き直した、それが以下です... + - 私は〆切がある、そのためこのパッチは今すぐ適用される必要がある + +カーネルコミュニティが大部分の伝統的なソフトウェアエンジニアリングの労 +働環境と異なるもう一つの点は、やりとりに顔を合わせないということです。 +email と irc を第一のコミュニケーションの形とする一つの利点は、性別や +民族の差別がないことです。Linux カーネルの職場環境は女性や少数民族を受 +容します。なぜなら、email アドレスによってのみあなたが認識されるからで +す。 +国際的な側面からも活動領域を均等にするようにします。なぜならば、あなた +は人の名前で性別を想像できないからです。ある男性が アンドレアという名 +前で、女性の名前は パット かもしれません (訳注 Andrea は米国では女性、 +それ以外(欧州など)では男性名として使われることが多い。同様に、Pat は +Patricia (主に女性名)や Patrick (主に男性名)の略称)。 +Linux カーネルの活動をして、意見を表明したことがある大部分の女性は、前 +向きな経験をもっています。 + +言葉の壁は英語が得意でない一部の人には問題になります。 +メーリングリストの中できちんとアイディアを交換するには、相当うまく英語 +を操れる必要があることもあります。そのため、あなたは自分のメール +を送る前に英語で意味が通じているかをチェックすることをお薦めします。 + +変更を分割する +--------------------- + +Linux カーネルコミュニティは、一度に大量のコードの塊を喜んで受容するこ +とはありません。変更は正確に説明される必要があり、議論され、小さい、個 +別の部分に分割する必要があります。これはこれまで多くの会社がやり慣れて +きたことと全く正反対のことです。あなたのプロポーザルは、開発プロセスのと +ても早い段階から紹介されるべきです。そうすれば あなたは自分のやってい +ることにフィードバックを得られます。これは、コミュニティからみれば、あ +なたが彼らと一緒にやっているように感じられ、単にあなたの提案する機能の +ゴミ捨て場として使っているのではない、と感じられるでしょう。 +しかし、一度に 50 もの email をメーリングリストに送りつけるようなことは +やってはいけません、あなたのパッチ群はいつもどんな時でもそれよりは小さ +くなければなりません。 + +パッチを分割する理由は以下です- + +1) 小さいパッチはあなたのパッチが適用される見込みを大きくします、カー + ネルの人達はパッチが正しいかどうかを確認する時間や労力をかけないか + らです。5行のパッチはメンテナがたった1秒見るだけで適用できます。し + かし、500行のパッチは、正しいことをレビューするのに数時間かかるかも + しれません(時間はパッチのサイズなどにより指数関数に比例してかかりま + す) + 小さいパッチは何かあったときにデバッグもとても簡単になります。パッ + チを1個1個取り除くのは、とても大きなパッチを当てた後に(かつ、何かお + かしくなった後で)解剖するのに比べればとても簡単です。 + +2) 小さいパッチを送るだけでなく、送るまえに、書き直して、シンプルにす + る(もしくは、単に順番を変えるだけでも)ことも、とても重要です。 + +以下はカーネル開発者の Al Viro のたとえ話しです: + + "生徒の数学の宿題を採点する先生のことを考えてみてください、先 + 生は生徒が解に到達するまでの試行錯誤をみたいとは思わないでしょ + う。先生は簡潔な最高の解をみたいのです。良い生徒はこれを知って + おり、そして最終解の前の中間作業を提出することは決してないので + す" + カーネル開発でもこれは同じです。メンテナー達とレビューア達は、 + 問題を解決する解の背後になる思考プロセスをみたいとは思いません。 + 彼らは単純であざやかな解決方法をみたいのです。 + +あざやかな解を説明するのと、コミュニティと共に仕事をし、未解決の仕事を +議論することのバランスをキープするのは難しいかもしれません。 +ですから、開発プロセスの早期段階で改善のためのフィードバックをもらうよ +うにするのもいいですが、変更点を小さい部分に分割して全体ではまだ完成し +ていない仕事を(部分的に)取り込んでもらえるようにすることもいいことです。 + +また、でき上がっていないものや、"将来直す" ようなパッチを、本流に含め +てもらうように送っても、それは受け付けられないことを理解してください。 + +あなたの変更を正当化する +------------------- + +あなたのパッチを分割するのと同時に、なぜその変更を追加しなければならな +いかを Linux コミュニティに知らせることはとても重要です。新機能は必要 +性と有用性で正当化されなければなりません。 + +あなたの変更の説明 +-------------------- + +あなたのパッチを送付する場合には、メールの中のテキストで何を言うかにつ +いて、特別に注意を払ってください。この情報はパッチの ChangeLog に使わ +れ、いつも皆がみられるように保管されます。これは次のような項目を含め、 +パッチを完全に記述するべきです- + + - なぜ変更が必要か + - パッチ全体の設計アプローチ + - 実装の詳細 + - テスト結果 + +これについて全てがどのようにあるべきかについての詳細は、以下のドキュメ +ントの ChangeLog セクションをみてください- + "The Perfect Patch" + http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + +これらのどれもが、時にはとても困難です。これらの慣例を完璧に実施するに +は数年かかるかもしれません。これは継続的な改善のプロセスであり、そのた +めには多数の忍耐と決意を必要とするものです。でも、諦めないで、これは可 +能なことです。多数の人がすでにできていますし、彼らも皆最初はあなたと同 +じところからスタートしたのですから。 + +Paolo Ciarrocchi に感謝、彼は彼の書いた "Development Process" +(http://linux.tar.bz/articles/2.6-development_process)セクショ +ンをこのテキストの原型にすることを許可してくれました。 +Rundy Dunlap と Gerrit Huizenga はメーリングリストでやるべきこととやっ +てはいけないことのリストを提供してくれました。 +以下の人々のレビュー、コメント、貢献に感謝。 +Pat Mochel, Hanna Linder, Randy Dunlap, Kay Sievers, +Vojtech Pavlik, Jan Kara, Josh Boyer, Kees Cook, Andrew Morton, Andi +Kleen, Vadim Lobanov, Jesper Juhl, Adrian Bunk, Keri Harris, Frans Pop, +David A. Wheeler, Junio Hamano, Michael Kerrisk, と Alex Shepard +彼らの支援なしでは、このドキュメントはできなかったでしょう。 + +Maintainer: Greg Kroah-Hartman <greg@kroah.com> diff --git a/Documentation/ja_JP/stable_api_nonsense.txt b/Documentation/ja_JP/stable_api_nonsense.txt new file mode 100644 index 00000000000..b3f2b27f088 --- /dev/null +++ b/Documentation/ja_JP/stable_api_nonsense.txt @@ -0,0 +1,263 @@ +NOTE: +This is a Japanese translated version of +"Documentation/stable_api_nonsense.txt". +This one is maintained by +IKEDA, Munehiro <m-ikeda@ds.jp.nec.com> +and JF Project team <http://www.linux.or.jp/JF/>. +If you find difference with original file or problem in translation, +please contact the maintainer of this file or JF project. + +Please also note that purpose of this file is easier to read for non +English natives and not to be intended to fork. So, if you have any +comments or updates of this file, please try to update +Original(English) file at first. + +================================== +これは、 +linux-2.6.22-rc4/Documentation/stable_api_nonsense.txt の和訳 +です。 +翻訳団体: JF プロジェクト < http://www.linux.or.jp/JF/ > +翻訳日 : 2007/06/11 +原著作者: Greg Kroah-Hartman < greg at kroah dot com > +翻訳者 : 池田 宗広 < m-ikeda at ds dot jp dot nec dot com > +校正者 : Masanori Kobayashi さん < zap03216 at nifty dot ne dot jp > + Seiji Kaneko さん < skaneko at a2 dot mbn dot or dot jp > +================================== + + + +Linux カーネルのドライバインターフェース +(あなたの質問すべてに対する回答とその他諸々) + +Greg Kroah-Hartman <greg at kroah dot com> + + +この文書は、なぜ Linux ではバイナリカーネルインターフェースが定義 +されていないのか、またはなぜ不変のカーネルインターフェースを持たな +いのか、ということを説明するために書かれた。ここでの話題は「カーネ +ル内部の」インターフェースについてであり、ユーザー空間とのインター +フェースではないことを理解してほしい。カーネルとユーザー空間とのイ +ンターフェースとはアプリケーションプログラムが使用するものであり、 +つまりシステムコールのインターフェースがこれに当たる。これは今まで +長きに渡り、かつ今後も「まさしく」不変である。私は確か 0.9 か何か +より前のカーネルを使ってビルドした古いプログラムを持っているが、そ +れは最新の 2.6 カーネルでもきちんと動作する。ユーザー空間とのイン +ターフェースは、ユーザーとアプリケーションプログラマが不変性を信頼 +してよいものの一つである。 + + +要旨 +---- + +あなたは不変のカーネルインターフェースが必要だと考えているかもしれ +ないが、実際のところはそうではない。あなたは必要としているものが分 +かっていない。あなたが必要としているものは安定して動作するドライバ +であり、それはドライバがメインのカーネルツリーに含まれる場合のみ得 +ることができる。ドライバがメインのカーネルツリーに含まれていると、 +他にも多くの良いことがある。それは、Linux をより強固で、安定な、成 +熟したオペレーティングシステムにすることができるということだ。これ +こそ、そもそもあなたが Linux を使う理由のはずだ。 + + +はじめに +-------- + +カーネル内部のインターフェース変更を心配しなければならないドライバ +を書きたいなどというのは、変わり者だけだ。この世界のほとんどの人は、 +そのようなドライバがどんなインターフェースを使っているかなど知らな +いし、そんなドライバのことなど全く気にもかけていない。 + + +まず初めに、クローズソースとか、ソースコードの隠蔽とか、バイナリの +みが配布される使い物にならない代物[訳注(1)]とか、実体はバイナリ +コードでそれを読み込むためのラッパー部分のみソースコードが公開され +ているとか、その他用語は何であれ GPL の下にソースコードがリリース +されていないカーネルドライバに関する法的な問題について、私は「いか +なる議論も」行うつもりがない。法的な疑問があるのならば、プログラマ +である私ではなく、弁護士に相談して欲しい。ここでは単に、技術的な問 +題について述べることにする。(法的な問題を軽視しているわけではない。 +それらは実際に存在するし、あなたはそれをいつも気にかけておく必要が +ある) + +訳注(1) +「使い物にならない代物」の原文は "blob" + + +さてここでは、バイナリカーネルインターフェースについてと、ソースレ +ベルでのインターフェースの不変性について、という二つの話題を取り上 +げる。この二つは互いに依存する関係にあるが、まずはバイナリインター +フェースについて議論を行いやっつけてしまおう。 + + +バイナリカーネルインターフェース +-------------------------------- + +もしソースレベルでのインターフェースが不変ならば、バイナリインター +フェースも当然のように不変である、というのは正しいだろうか?正しく +ない。Linux カーネルに関する以下の事実を考えてみてほしい。 + - あなたが使用するCコンパイラのバージョンによって、カーネル内部 + の構造体の配置構造は異なったものになる。また、関数は異なった方 + 法でカーネルに含まれることになるかもしれない(例えばインライン + 関数として扱われたり、扱われなかったりする)。個々の関数がどの + ようにコンパイルされるかはそれほど重要ではないが、構造体のパデ + ィングが異なるというのは非常に重要である。 + - あなたがカーネルのビルドオプションをどのように設定するかによっ + て、カーネルには広い範囲で異なった事態が起こり得る。 + - データ構造は異なるデータフィールドを持つかもしれない + - いくつかの関数は全く実装されていない状態になり得る + (例:SMP向けではないビルドでは、いくつかのロックは中身が + カラにコンパイルされる) + - カーネル内のメモリは、異なった方法で配置され得る。これはビ + ルドオプションに依存している。 + - Linux は様々な異なるプロセッサアーキテクチャ上で動作する。 + あるアーキテクチャ用のバイナリドライバを、他のアーキテクチャで + 正常に動作させる方法はない。 + + +ある特定のカーネル設定を使用し、カーネルをビルドしたのと正確に同じ +Cコンパイラを使用して単にカーネルモジュールをコンパイルするだけで +も、あなたはこれらいくつもの問題に直面することになる。ある特定の +Linux ディストリビューションの、ある特定のリリースバージョン用にモ +ジュールを提供しようと思っただけでも、これらの問題を引き起こすには +十分である。にも関わらず Linux ディストリビューションの数と、サ +ポートするディストリビューションのリリース数を掛け算し、それら一つ +一つについてビルドを行ったとしたら、今度はリリースごとのビルドオプ +ションの違いという悪夢にすぐさま悩まされることになる。また、ディス +トリビューションの各リリースバージョンには、異なるハードウェア(プ +ロセッサタイプや種々のオプション)に対応するため、何種類かのカーネ +ルが含まれているということも理解して欲しい。従って、ある一つのリ +リースバージョンだけのためにモジュールを作成する場合でも、あなたは +何バージョンものモジュールを用意しなければならない。 + + +信じて欲しい。このような方法でサポートを続けようとするなら、あなた +はいずれ正気を失うだろう。遠い昔、私はそれがいかに困難なことか、身 +をもって学んだのだ・・・ + + +不変のカーネルソースレベルインターフェース +------------------------------------------ + +メインカーネルツリーに含まれていない Linux カーネルドライバを継続 +してサポートしていこうとしている人たちとの議論においては、これは極 +めて「引火性の高い」話題である。[訳注(2)] + +訳注(2) +「引火性の高い」の原文は "volatile"。 +volatile には「揮発性の」「爆発しやすい」という意味の他、「変わり +やすい」「移り気な」という意味がある。 +「(この話題は)爆発的に激しい論争を巻き起こしかねない」ということ +を、「(カーネルのソースレベルインターフェースは)移ろい行くもので +ある」ということを連想させる "volatile" という単語で表現している。 + + +Linux カーネルの開発は継続的に速いペースで行われ、決して歩みを緩め +ることがない。その中でカーネル開発者達は、現状のインターフェースに +あるバグを見つけ、より良い方法を考え出す。彼らはやがて、現状のイン +ターフェースがより正しく動作するように修正を行う。その過程で関数の +名前は変更されるかもしれず、構造体は大きく、または小さくなるかもし +れず、関数の引数は検討しなおされるかもしれない。そのような場合、引 +き続き全てが正常に動作するよう、カーネル内でこれらのインターフェー +スを使用している個所も全て同時に修正される。 + + +具体的な例として、カーネル内の USB インターフェースを挙げる。USB +サブシステムはこれまでに少なくとも3回の書き直しが行われ、その結果 +インターフェースが変更された。これらの書き直しはいくつかの異なった +問題を修正するために行われた。 + - 同期的データストリームが非同期に変更された。これにより多数のド + ライバを単純化でき、全てのドライバのスループットが向上した。今 + やほとんど全ての USB デバイスは、考えられる最高の速度で動作し + ている。 + - USB ドライバが USB サブシステムのコアから行う、データパケット + 用のメモリ確保方法が変更された。これに伴い、いくつもの文書化さ + れたデッドロック条件を回避するため、全ての USB ドライバはより + 多くの情報を USB コアに提供しなければならないようになっている。 + + +このできごとは、数多く存在するクローズソースのオペレーティングシス +テムとは全く対照的だ。それらは長期に渡り古い USB インターフェース +をメンテナンスしなければならない。古いインターフェースが残ることで、 +新たな開発者が偶然古いインターフェースを使い、正しくない方法で開発 +を行ってしまう可能性が生じる。これによりシステムの安定性は危険にさ +らされることになる。 + + +上に挙げたどちらの例においても、開発者達はその変更が重要かつ必要で +あることに合意し、比較的楽にそれを実行した。もし Linux がソースレ +ベルでインターフェースの不変性を保証しなければならないとしたら、新 +しいインターフェースを作ると同時に、古い、問題のある方を今後ともメ +ンテナンスするという余計な仕事を USB の開発者にさせなければならな +い。Linux の USB 開発者は、自分の時間を使って仕事をしている。よっ +て、価値のない余計な仕事を報酬もなしに実行しろと言うことはできない。 + + +セキュリティ問題も、Linux にとっては非常に重要である。ひとたびセキ +ュリティに関する問題が発見されれば、それは極めて短期間のうちに修正 +される。セキュリティ問題の発生を防ぐための修正は、カーネルの内部イ +ンターフェースの変更を何度も引き起こしてきた。その際同時に、変更さ +れたインターフェースを使用する全てのドライバもまた変更された。これ +により問題が解消し、将来偶然に問題が再発してしまわないことが保証さ +れる。もし内部インターフェースの変更が許されないとしたら、このよう +にセキュリティ問題を修正し、将来再発しないことを保証することなど不 +可能なのだ。 + + +カーネルのインターフェースは時が経つにつれクリーンナップを受ける。 +誰も使っていないインターフェースは削除される。これにより、可能な限 +りカーネルが小さく保たれ、現役の全てのインターフェースが可能な限り +テストされることを保証しているのだ。(使われていないインターフェー +スの妥当性をテストすることは不可能と言っていいだろう) + + + +これから何をすべきか +----------------------- + +では、もしメインのカーネルツリーに含まれない Linux カーネルドライ +バがあったとして、あなたは、つまり開発者は何をするべきだろうか?全 +てのディストリビューションの全てのカーネルバージョン向けにバイナリ +のドライバを供給することは悪夢であり、カーネルインターフェースの変 +更を追いかけ続けることもまた過酷な仕事だ。 + + +答えは簡単。そのドライバをメインのカーネルツリーに入れてしまえばよ +い。(ここで言及しているのは、GPL に従って公開されるドライバのこと +だということに注意してほしい。あなたのコードがそれに該当しないなら +ば、さよなら。幸運を祈ります。ご自分で何とかしてください。Andrew +と Linus からのコメント<Andrew と Linus のコメントへのリンクをこ +こに置く>をどうぞ)ドライバがメインツリーに入れば、カーネルのイン +ターフェースが変更された場合、変更を行った開発者によってドライバも +修正されることになるだろう。あなたはほとんど労力を払うことなしに、 +常にビルド可能できちんと動作するドライバを手に入れることができる。 + + +ドライバをメインのカーネルツリーに入れると、非常に好ましい以下の効 +果がある。 + - ドライバの品質が向上する一方で、(元の開発者にとっての)メンテ + ナンスコストは下がる。 + - あなたのドライバに他の開発者が機能を追加してくれる。 + - 誰かがあなたのドライバにあるバグを見つけ、修正してくれる。 + - 誰かがあなたのドライバにある改善点を見つけてくれる。 + - 外部インターフェースが変更されドライバの更新が必要になった場合、 + 誰かがあなたの代わりに更新してくれる。 + - ドライバを入れてくれとディストロに頼まなくても、そのドライバは + 全ての Linux ディストリビューションに自動的に含まれてリリース + される。 + + +Linux では、他のどのオペレーティングシステムよりも数多くのデバイス +が「そのまま」使用できるようになった。また Linux は、どのオペレー +ティングシステムよりも数多くのプロセッサアーキテクチャ上でそれらの +デバイスを使用することができるようにもなった。このように、Linux の +開発モデルは実証されており、今後も間違いなく正しい方向へと進んでい +くだろう。:) + + + +------ + +この文書の初期の草稿に対し、Randy Dunlap, Andrew Morton, David +Brownell, Hanna Linder, Robert Love, Nishanth Aravamudan から査読 +と助言を頂きました。感謝申し上げます。 + diff --git a/Documentation/zh_CN/HOWTO b/Documentation/zh_CN/HOWTO new file mode 100644 index 00000000000..48fc67bfbe3 --- /dev/null +++ b/Documentation/zh_CN/HOWTO @@ -0,0 +1,536 @@ +Chinese translated version of Documentation/HOWTO + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have problem +communicating in English you can also ask the Chinese maintainer for +help. Contact the Chinese maintainer, if this translation is outdated +or there is problem with translation. + +Maintainer: Greg Kroah-Hartman <greg@kroah.com> +Chinese maintainer: Li Yang <leoli@freescale.com> +--------------------------------------------------------------------- +Documentation/HOWTO 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> +中文版维护者: 李阳 Li Yang <leoli@freescale.com> +中文版翻译者: 李阳 Li Yang <leoli@freescale.com> +中文版校译者: 钟宇 TripleX Chung <xxx.phy@gmail.com> + 陈琦 Maggie Chen <chenqi@beyondsoft.com> + 王聪 Wang Cong <xiyou.wangcong@gmail.com> + +以下为正文 +--------------------------------------------------------------------- + +如何参与Linux内核开发 +--------------------- + +这是一篇将如何参与Linux内核开发的相关问题一网打尽的终极秘笈。它将指导你 +成为一名Linux内核开发者,并且学会如何同Linux内核开发社区合作。它尽可能不 +包括任何关于内核编程的技术细节,但会给你指引一条获得这些知识的正确途径。 + +如果这篇文章中的任何内容不再适用,请给文末列出的文件维护者发送补丁。 + + +入门 +---- + +你想了解如何成为一名Linux内核开发者?或者老板吩咐你“给这个设备写个Linux +驱动程序”?这篇文章的目的就是教会你达成这些目标的全部诀窍,它将描述你需 +要经过的流程以及给出如何同内核社区合作的一些提示。它还将试图解释内核社区 +为何这样运作。 + +Linux内核大部分是由C语言写成的,一些体系结构相关的代码用到了汇编语言。要 +参与内核开发,你必须精通C语言。除非你想为某个架构开发底层代码,否则你并 +不需要了解(任何体系结构的)汇编语言。下面列举的书籍虽然不能替代扎实的C +语言教育和多年的开发经验,但如果需要的话,做为参考还是不错的: + - "The C Programming Language" by Kernighan and Ritchie [Prentice Hall] + 《C程序设计语言(第2版·新版)》(徐宝文 李志 译)[机械工业出版社] + - "Practical C Programming" by Steve Oualline [O'Reilly] + 《实用C语言编程(第三版)》(郭大海 译)[中国电力出版社] + - "C: A Reference Manual" by Harbison and Steele [Prentice Hall] + 《C语言参考手册(原书第5版)》(邱仲潘 等译)[机械工业出版社] + +Linux内核使用GNU C和GNU工具链开发。虽然它遵循ISO C89标准,但也用到了一些 +标准中没有定义的扩展。内核是自给自足的C环境,不依赖于标准C库的支持,所以 +并不支持C标准中的部分定义。比如long long类型的大数除法和浮点运算就不允许 +使用。有时候确实很难弄清楚内核对工具链的要求和它所使用的扩展,不幸的是目 +前还没有明确的参考资料可以解释它们。请查阅gcc信息页(使用“info gcc”命令 +显示)获得一些这方面信息。 + +请记住你是在学习怎么和已经存在的开发社区打交道。它由一群形形色色的人组成, +他们对代码、风格和过程有着很高的标准。这些标准是在长期实践中总结出来的, +适应于地理上分散的大型开发团队。它们已经被很好得整理成档,建议你在开发 +之前尽可能多的学习这些标准,而不要期望别人来适应你或者你公司的行为方式。 + + +法律问题 +-------- + +Linux内核源代码都是在GPL(通用公共许可证)的保护下发布的。要了解这种许可 +的细节请查看源代码主目录下的COPYING文件。如果你对它还有更深入问题请联系 +律师,而不要在Linux内核邮件组上提问。因为邮件组里的人并不是律师,不要期 +望他们的话有法律效力。 + +对于GPL的常见问题和解答,请访问以下链接: + http://www.gnu.org/licenses/gpl-faq.html + + +文档 +---- + +Linux内核代码中包含有大量的文档。这些文档对于学习如何与内核社区互动有着 +不可估量的价值。当一个新的功能被加入内核,最好把解释如何使用这个功能的文 +档也放进内核。当内核的改动导致面向用户空间的接口发生变化时,最好将相关信 +息或手册页(manpages)的补丁发到mtk-manpages@gmx.net,以向手册页(manpages) +的维护者解释这些变化。 + +以下是内核代码中需要阅读的文档: + README + 文件简要介绍了Linux内核的背景,并且描述了如何配置和编译内核。内核的 + 新用户应该从这里开始。 + + Documentation/Changes + 文件给出了用来编译和使用内核所需要的最小软件包列表。 + + Documentation/CodingStyle + 描述Linux内核的代码风格和理由。所有新代码需要遵守这篇文档中定义的规 + 范。大多数维护者只会接收符合规定的补丁,很多人也只会帮忙检查符合风格 + 的代码。 + + Documentation/SubmittingPatches + Documentation/SubmittingDrivers + 这两份文档明确描述如何创建和发送补丁,其中包括(但不仅限于): + - 邮件内容 + - 邮件格式 + - 选择收件人 + 遵守这些规定并不能保证提交成功(因为所有补丁需要通过严格的内容和风格 + 审查),但是忽视他们几乎就意味着失败。 + + 其他关于如何正确地生成补丁的优秀文档包括: + "The Perfect Patch" + http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + "Linux kernel patch submission format" + http://linux.yyz.us/patch-format.html + + Documentation/stable_api_nonsense.txt + 论证内核为什么特意不包括稳定的内核内部API,也就是说不包括像这样的特 + 性: + - 子系统中间层(为了兼容性?) + - 在不同操作系统间易于移植的驱动程序 + - 减缓(甚至阻止)内核代码的快速变化 + 这篇文档对于理解Linux的开发哲学至关重要。对于将开发平台从其他操作系 + 统转移到Linux的人来说也很重要。 + + Documentation/SecurityBugs + 如果你认为自己发现了Linux内核的安全性问题,请根据这篇文档中的步骤来 + 提醒其他内核开发者并帮助解决这个问题。 + + Documentation/ManagementStyle + 描述内核维护者的工作方法及其共有特点。这对于刚刚接触内核开发(或者对 + 它感到好奇)的人来说很重要,因为它解释了很多对于内核维护者独特行为的 + 普遍误解与迷惑。 + + Documentation/stable_kernel_rules.txt + 解释了稳定版内核发布的规则,以及如何将改动放入这些版本的步骤。 + + Documentation/kernel-docs.txt + 有助于内核开发的外部文档列表。如果你在内核自带的文档中没有找到你想找 + 的内容,可以查看这些文档。 + + Documentation/applying-patches.txt + 关于补丁是什么以及如何将它打在不同内核开发分支上的好介绍 + +内核还拥有大量从代码自动生成的文档。它包含内核内部API的全面介绍以及如何 +妥善处理加锁的规则。生成的文档会放在 Documentation/DocBook/目录下。在内 +核源码的主目录中使用以下不同命令将会分别生成PDF、Postscript、HTML和手册 +页等不同格式的文档: + make pdfdocs + make psdocs + make htmldocs + make mandocs + + +如何成为内核开发者 +------------------ +如果你对Linux内核开发一无所知,你应该访问“Linux内核新手”计划: + http://kernelnewbies.org +它拥有一个可以问各种最基本的内核开发问题的邮件列表(在提问之前一定要记得 +查找已往的邮件,确认是否有人已经回答过相同的问题)。它还拥有一个可以获得 +实时反馈的IRC聊天频道,以及大量对于学习Linux内核开发相当有帮助的文档。 + +网站简要介绍了源代码组织结构、子系统划分以及目前正在进行的项目(包括内核 +中的和单独维护的)。它还提供了一些基本的帮助信息,比如如何编译内核和打补 +丁。 + +如果你想加入内核开发社区并协助完成一些任务,却找不到从哪里开始,可以访问 +“Linux内核房管员”计划: + http://janitor.kernelnewbies.org/ +这是极佳的起点。它提供一个相对简单的任务列表,列出内核代码中需要被重新 +整理或者改正的地方。通过和负责这个计划的开发者们一同工作,你会学到将补丁 +集成进内核的基本原理。如果还没有决定下一步要做什么的话,你还可能会得到方 +向性的指点。 + +如果你已经有一些现成的代码想要放到内核中,但是需要一些帮助来使它们拥有正 +确的格式。请访问“内核导师”计划。这个计划就是用来帮助你完成这个目标的。它 +是一个邮件列表,地址如下: + http://selenic.com/mailman/listinfo/kernel-mentors + +在真正动手修改内核代码之前,理解要修改的代码如何运作是必需的。要达到这个 +目的,没什么办法比直接读代码更有效了(大多数花招都会有相应的注释),而且 +一些特制的工具还可以提供帮助。例如,“Linux代码交叉引用”项目就是一个值得 +特别推荐的帮助工具,它将源代码显示在有编目和索引的网页上。其中一个更新及 +时的内核源码库,可以通过以下地址访问: + http://sosdg.org/~coywolf/lxr/ + + +开发流程 +-------- + +目前Linux内核开发流程包括几个“主内核分支”和很多子系统相关的内核分支。这 +些分支包括: + - 2.6.x主内核源码树 + - 2.6.x.y -stable内核源码树 + - 2.6.x -git内核补丁集 + - 2.6.x -mm内核补丁集 + - 子系统相关的内核源码树和补丁集 + + +2.6.x内核主源码树 +----------------- +2.6.x内核是由Linus Torvalds(Linux的创造者)亲自维护的。你可以在 +kernel.org网站的pub/linux/kernel/v2.6/目录下找到它。它的开发遵循以下步 +骤: + - 每当一个新版本的内核被发布,为期两周的集成窗口将被打开。在这段时间里 + 维护者可以向Linus提交大段的修改,通常这些修改已经被放到-mm内核中几个 + 星期了。提交大量修改的首选方式是使用git工具(内核的代码版本管理工具 + ,更多的信息可以在http://git.or.cz/获取),不过使用普通补丁也是可以 + 的。 + - 两个星期以后-rc1版本内核发布。之后只有不包含可能影响整个内核稳定性的 + 新功能的补丁才可能被接受。请注意一个全新的驱动程序(或者文件系统)有 + 可能在-rc1后被接受是因为这样的修改完全独立,不会影响其他的代码,所以 + 没有造成内核退步的风险。在-rc1以后也可以用git向Linus提交补丁,不过所 + 有的补丁需要同时被发送到相应的公众邮件列表以征询意见。 + - 当Linus认为当前的git源码树已经达到一个合理健全的状态足以发布供人测试 + 时,一个新的-rc版本就会被发布。计划是每周都发布新的-rc版本。 + - 这个过程一直持续下去直到内核被认为达到足够稳定的状态,持续时间大概是 + 6个星期。 + +关于内核发布,值得一提的是Andrew Morton在linux-kernel邮件列表中如是说: + “没有人知道新内核何时会被发布,因为发布是根据已知bug的情况来决定 + 的,而不是根据一个事先制定好的时间表。” + + +2.6.x.y -stable(稳定版)内核源码树 +----------------------------------- +由4个数字组成的内核版本号说明此内核是-stable版本。它们包含基于2.6.x版本 +内核的相对较小且至关重要的修补,这些修补针对安全性问题或者严重的内核退步。 + +这种版本的内核适用于那些期望获得最新的稳定版内核并且不想参与测试开发版或 +者实验版的用户。 + +如果没有2.6.x.y版本内核存在,那么最新的2.6.x版本内核就相当于是当前的稳定 +版内核。 + +2.6.x.y版本由“稳定版”小组(邮件地址<stable@kernel.org>)维护,一般隔周发 +布新版本。 + +内核源码中的Documentation/stable_kernel_rules.txt文件具体描述了可被稳定 +版内核接受的修改类型以及发布的流程。 + + +2.6.x -git补丁集 +---------------- +Linus的内核源码树的每日快照,这个源码树是由git工具管理的(由此得名)。这 +些补丁通常每天更新以反映Linus的源码树的最新状态。它们比-rc版本的内核源码 +树更具试验性质,因为这个补丁集是全自动生成的,没有任何人来确认其是否真正 +健全。 + + +2.6.x -mm补丁集 +--------------- +这是由Andrew Morton维护的试验性内核补丁集。Andrew将所有子系统的内核源码 +和补丁拼凑到一起,并且加入了大量从linux-kernel邮件列表中采集的补丁。这个 +源码树是新功能和补丁的试炼场。当补丁在-mm补丁集里证明了其价值以后Andrew +或者相应子系统的维护者会将补丁发给Linus以便集成进主内核源码树。 + +在将所有新补丁发给Linus以集成到主内核源码树之前,我们非常鼓励先把这些补 +丁放在-mm版内核源码树中进行测试。 + +这些内核版本不适合在需要稳定运行的系统上运行,因为运行它们比运行任何其他 +内核分支都更具有风险。 + +如果你想为内核开发进程提供帮助,请尝试并使用这些内核版本,并在 +linux-kernel邮件列表中提供反馈,告诉大家你遇到了问题还是一切正常。 + +通常-mm版补丁集不光包括这些额外的试验性补丁,还包括发布时-git版主源码树 +中的改动。 + +-mm版内核没有固定的发布周期,但是通常在每两个-rc版内核发布之间都会有若干 +个-mm版内核发布(一般是1至3个)。 + + +子系统相关内核源码树和补丁集 +---------------------------- +相当一部分内核子系统开发者会公开他们自己的开发源码树,以便其他人能了解内 +核的不同领域正在发生的事情。如上所述,这些源码树会被集成到-mm版本内核中。 + +下面是目前可用的一些内核源码树的列表: + 通过git管理的源码树: + - Kbuild开发源码树, Sam Ravnborg <sam@ravnborg.org> + git.kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git + + - ACPI开发源码树, Len Brown <len.brown@intel.com> + git.kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git + + - 块设备开发源码树, Jens Axboe <axboe@suse.de> + git.kernel.org:/pub/scm/linux/kernel/git/axboe/linux-2.6-block.git + + - DRM开发源码树, Dave Airlie <airlied@linux.ie> + git.kernel.org:/pub/scm/linux/kernel/git/airlied/drm-2.6.git + + - ia64开发源码树, Tony Luck <tony.luck@intel.com> + git.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git + + - ieee1394开发源码树, Jody McIntyre <scjody@modernduck.com> + git.kernel.org:/pub/scm/linux/kernel/git/scjody/ieee1394.git + + - infiniband开发源码树, Roland Dreier <rolandd@cisco.com> + git.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git + + - libata开发源码树, Jeff Garzik <jgarzik@pobox.com> + git.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git + + - 网络驱动程序开发源码树, Jeff Garzik <jgarzik@pobox.com> + git.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git + + - pcmcia开发源码树, Dominik Brodowski <linux@dominikbrodowski.net> + git.kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git + + - SCSI开发源码树, James Bottomley <James.Bottomley@SteelEye.com> + git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git + + 使用quilt管理的补丁集: + - USB, PCI, 驱动程序核心和I2C, Greg Kroah-Hartman <gregkh@suse.de> + kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/ + - x86-64, 部分i386, Andi Kleen <ak@suse.de> + ftp.firstfloor.org:/pub/ak/x86_64/quilt/ + + 其他内核源码树可以在http://git.kernel.org的列表中和MAINTAINERS文件里 + 找到。 + +报告bug +------- + +bugzilla.kernel.org是Linux内核开发者们用来跟踪内核Bug的网站。我们鼓励用 +户在这个工具中报告找到的所有bug。如何使用内核bugzilla的细节请访问: + http://test.kernel.org/bugzilla/faq.html + +内核源码主目录中的REPORTING-BUGS文件里有一个很好的模板。它指导用户如何报 +告可能的内核bug以及需要提供哪些信息来帮助内核开发者们找到问题的根源。 + + +利用bug报告 +----------- + +练习内核开发技能的最好办法就是修改其他人报告的bug。你不光可以帮助内核变 +得更加稳定,还可以学会如何解决实际问题从而提高自己的技能,并且让其他开发 +者感受到你的存在。修改bug是赢得其他开发者赞誉的最好办法,因为并不是很多 +人都喜欢浪费时间去修改别人报告的bug。 + +要尝试修改已知的bug,请访问http://bugzilla.kernel.org网址。如果你想获得 +最新bug的通知,可以订阅bugme-new邮件列表(只有新的bug报告会被寄到这里) +或者订阅bugme-janitor邮件列表(所有bugzilla的变动都会被寄到这里)。 + + http://lists.osdl.org/mailman/listinfo/bugme-new + http://lists.osdl.org/mailman/listinfo/bugme-janitors + + +邮件列表 +-------- + +正如上面的文档所描述,大多数的骨干内核开发者都加入了Linux Kernel邮件列 +表。如何订阅和退订列表的细节可以在这里找到: + http://vger.kernel.org/vger-lists.html#linux-kernel +网上很多地方都有这个邮件列表的存档(archive)。可以使用搜索引擎来找到这些 +存档。比如: + http://dir.gmane.org/gmane.linux.kernel +在发信之前,我们强烈建议你先在存档中搜索你想要讨论的问题。很多已经被详细 +讨论过的问题只在邮件列表的存档中可以找到。 + +大多数内核子系统也有自己独立的邮件列表来协调各自的开发工作。从 +MAINTAINERS文件中可以找到不同话题对应的邮件列表。 + +很多邮件列表架设在kernel.org服务器上。这些列表的信息可以在这里找到: + http://vger.kernel.org/vger-lists.html + +在使用这些邮件列表时,请记住保持良好的行为习惯。下面的链接提供了与这些列 +表(或任何其它邮件列表)交流的一些简单规则,虽然内容有点滥竽充数。 + http://www.albion.com/netiquette/ + +当有很多人回复你的邮件时,邮件的抄送列表会变得很长。请不要将任何人从抄送 +列表中删除,除非你有足够的理由这么做。也不要只回复到邮件列表。请习惯于同 +一封邮件接收两次(一封来自发送者一封来自邮件列表),而不要试图通过添加一 +些奇特的邮件头来解决这个问题,人们不会喜欢的。 + +记住保留你所回复内容的上下文和源头。在你回复邮件的顶部保留“某某某说到……” +这几行。将你的评论加在被引用的段落之间而不要放在邮件的顶部。 + +如果你在邮件中附带补丁,请确认它们是可以直接阅读的纯文本(如 +Documentation/SubmittingPatches文档中所述)。内核开发者们不希望遇到附件 +或者被压缩了的补丁。只有这样才能保证他们可以直接评论你的每行代码。请确保 +你使用的邮件发送程序不会修改空格和制表符。一个防范性的测试方法是先将邮件 +发送给自己,然后自己尝试是否可以顺利地打上收到的补丁。如果测试不成功,请 +调整或者更换你的邮件发送程序直到它正确工作为止。 + +总而言之,请尊重其他的邮件列表订阅者。 + + +同内核社区合作 +---------------- + +内核社区的目标就是提供尽善尽美的内核。所以当你提交补丁期望被接受进内核的 +时候,它的技术价值以及其他方面都将被评审。那么你可能会得到什么呢? + - 批评 + - 评论 + - 要求修改 + - 要求证明修改的必要性 + - 沉默 + +要记住,这些是把补丁放进内核的正常情况。你必须学会听取对补丁的批评和评论, +从技术层面评估它们,然后要么重写你的补丁要么简明扼要地论证修改是不必要 +的。如果你发的邮件没有得到任何回应,请过几天后再试一次,因为有时信件会湮 +没在茫茫信海中。 + +你不应该做的事情: + - 期望自己的补丁不受任何质疑就直接被接受 + - 翻脸 + - 忽略别人的评论 + - 没有按照别人的要求做任何修改就重新提交 + +在一个努力追寻最好技术方案的社区里,对于一个补丁有多少好处总会有不同的见 +解。你必须要抱着合作的态度,愿意改变自己的观点来适应内核的风格。或者至少 +愿意去证明你的想法是有价值的。记住,犯错误是允许的,只要你愿意朝着正确的 +方案去努力。 + +如果你的第一个补丁换来的是一堆修改建议,这是很正常的。这并不代表你的补丁 +不会被接受,也不意味着有人和你作对。你只需要改正所有提出的问题然后重新发 +送你的补丁。 + +内核社区和公司文化的差异 +------------------------ + +内核社区的工作模式同大多数传统公司开发队伍的工作模式并不相同。下面这些例 +子,可以帮助你避免某些可能发生问题: + 用这些话介绍你的修改提案会有好处: + - 它同时解决了多个问题 + - 它删除了2000行代码 + - 这是补丁,它已经解释了我想要说明的 + - 我在5种不同的体系结构上测试过它…… + - 这是一系列小补丁用来…… + - 这个修改提高了普通机器的性能…… + + 应该避免如下的说法: + - 我们在AIX/ptx/Solaris就是这么做的,所以这么做肯定是好的…… + - 我做这行已经20年了,所以…… + - 为了我们公司赚钱考虑必须这么做 + - 这是我们的企业产品线所需要的 + - 这里是描述我观点的1000页设计文档 + - 这是一个5000行的补丁用来…… + - 我重写了现在乱七八糟的代码,这就是…… + - 我被规定了最后期限,所以这个补丁需要立刻被接受 + +另外一个内核社区与大部分传统公司的软件开发队伍不同的地方是无法面对面地交 +流。使用电子邮件和IRC聊天工具做为主要沟通工具的一个好处是性别和种族歧视 +将会更少。Linux内核的工作环境更能接受妇女和少数族群,因为每个人在别人眼 +里只是一个邮件地址。国际化也帮助了公平的实现,因为你无法通过姓名来判断人 +的性别。男人有可能叫李丽,女人也有可能叫王刚。大多数在Linux内核上工作过 +并表达过看法的女性对在linux上工作的经历都给出了正面的评价。 + +对于一些不习惯使用英语的人来说,语言可能是一个引起问题的障碍。在邮件列表 +中要正确地表达想法必需良好地掌握语言,所以建议你在发送邮件之前最好检查一 +下英文写得是否正确。 + + +拆分修改 +-------- + +Linux内核社区并不喜欢一下接收大段的代码。修改需要被恰当地介绍、讨论并且 +拆分成独立的小段。这几乎完全和公司中的习惯背道而驰。你的想法应该在开发最 +开始的阶段就让大家知道,这样你就可以及时获得对你正在进行的开发的反馈。这 +样也会让社区觉得你是在和他们协作,而不是仅仅把他们当作倾销新功能的对象。 +无论如何,你不要一次性地向邮件列表发送50封信,你的补丁序列应该永远用不到 +这么多。 + +将补丁拆开的原因如下: + +1) 小的补丁更有可能被接受,因为它们不需要太多的时间和精力去验证其正确性。 + 一个5行的补丁,可能在维护者看了一眼以后就会被接受。而500行的补丁则 + 需要数个小时来审查其正确性(所需时间随补丁大小增加大约呈指数级增长)。 + + 当出了问题的时候,小的补丁也会让调试变得非常容易。一个一个补丁地回溯 + 将会比仔细剖析一个被打上的大补丁(这个补丁破坏了其他东西)容易得多。 + +2)不光发送小的补丁很重要,在提交之前重新编排、化简(或者仅仅重新排列) + 补丁也是很重要的。 + +这里有内核开发者Al Viro打的一个比方: + “想象一个老师正在给学生批改数学作业。老师并不希望看到学生为了得 + 到正确解法所进行的尝试和产生的错误。他希望看到的是最干净最优雅的 + 解答。好学生了解这点,绝不会把最终解决之前的中间方案提交上去。” + + 内核开发也是这样。维护者和评审者不希望看到一个人在解决问题时的思 + 考过程。他们只希望看到简单和优雅的解决方案。 + +直接给出一流的解决方案,和社区一起协作讨论尚未完成的工作,这两者之间似乎 +很难找到一个平衡点。所以最好尽早开始收集有利于你进行改进的反馈;同时也要 +保证修改分成很多小块,这样在整个项目都准备好被包含进内核之前,其中的一部 +分可能会先被接收。 + +必须了解这样做是不可接受的:试图将未完成的工作提交进内核,然后再找时间修 +复。 + + +证明修改的必要性 +---------------- +除了将补丁拆成小块,很重要的一点是让Linux社区了解他们为什么需要这样修改。 +你必须证明新功能是有人需要的并且是有用的。 + + +记录修改 +-------- + +当你发送补丁的时候,需要特别留意邮件正文的内容。因为这里的信息将会做为补 +丁的修改记录(ChangeLog),会被一直保留以备大家查阅。它需要完全地描述补丁, +包括: + - 为什么需要这个修改 + - 补丁的总体设计 + - 实现细节 + - 测试结果 + +想了解它具体应该看起来像什么,请查阅以下文档中的“ChangeLog”章节: + “The Perfect Patch” + http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt + + +这些事情有时候做起来很难。要在任何方面都做到完美可能需要好几年时间。这是 +一个持续提高的过程,它需要大量的耐心和决心。只要不放弃,你一定可以做到。 +很多人已经做到了,而他们都曾经和现在的你站在同样的起点上。 + + +--------------- +感谢Paolo Ciarrocchi允许“开发流程”部分基于他所写的文章 +(http://linux.tar.bz/articles/2.6-development_process),感谢Randy +Dunlap和Gerrit Huizenga完善了应该说和不该说的列表。感谢Pat Mochel, Hanna +Linder, Randy Dunlap, Kay Sievers, Vojtech Pavlik, Jan Kara, Josh Boyer, +Kees Cook, Andrew Morton, Andi Kleen, Vadim Lobanov, Jesper Juhl, Adrian +Bunk, Keri Harris, Frans Pop, David A. Wheeler, Junio Hamano, Michael +Kerrisk和Alex Shepard的评审、建议和贡献。没有他们的帮助,这篇文档是不可 +能完成的。 + + + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> diff --git a/Documentation/zh_CN/stable_api_nonsense.txt b/Documentation/zh_CN/stable_api_nonsense.txt new file mode 100644 index 00000000000..c26a27d1ee7 --- /dev/null +++ b/Documentation/zh_CN/stable_api_nonsense.txt @@ -0,0 +1,157 @@ +Chinese translated version of Documentation/stable_api_nonsense.txt + +If you have any comment or update to the content, please contact the +original document maintainer directly. However, if you have problem +communicating in English you can also ask the Chinese maintainer for help. +Contact the Chinese maintainer, if this translation is outdated or there +is problem with translation. + +Maintainer: Greg Kroah-Hartman <greg@kroah.com> +Chinese maintainer: TripleX Chung <zhongyu@18mail.cn> +--------------------------------------------------------------------- +Documentation/stable_api_nonsense.txt 的中文翻译 + +如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文 +交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻 +译存在问题,请联系中文版维护者。 + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> +中文版维护者: 钟宇 TripleX Chung <zhongyu@18mail.cn> +中文版翻译者: 钟宇 TripleX Chung <zhongyu@18mail.cn> +中文版校译者: 李阳 Li Yang <leoli@freescale.com> +以下为正文 +--------------------------------------------------------------------- + +写作本文档的目的,是为了解释为什么Linux既没有二进制内核接口,也没有稳定 +的内核接口。这里所说的内核接口,是指内核里的接口,而不是内核和用户空间 +的接口。内核到用户空间的接口,是提供给应用程序使用的系统调用,系统调用 +在历史上几乎没有过变化,将来也不会有变化。我有一些老应用程序是在0.9版本 +或者更早版本的内核上编译的,在使用2.6版本内核的Linux发布上依然用得很好 +。用户和应用程序作者可以将这个接口看成是稳定的。 + + +执行纲要 +-------- + +你也许以为自己想要稳定的内核接口,但是你不清楚你要的实际上不是它。你需 +要的其实是稳定的驱动程序,而你只有将驱动程序放到公版内核的源代码树里, +才有可能达到这个目的。而且这样做还有很多其它好处,正是因为这些好处使得 +Linux能成为强壮,稳定,成熟的操作系统,这也是你最开始选择Linux的原因。 + + +入门 +----- + +只有那些写驱动程序的“怪人”才会担心内核接口的改变,对广大用户来说,既 +看不到内核接口,也不需要去关心它。 + +首先,我不打算讨论关于任何非GPL许可的内核驱动的法律问题,这些非GPL许可 +的驱动程序包括不公开源代码,隐藏源代码,二进制或者是用源代码包装,或者 +是其它任何形式的不能以GPL许可公开源代码的驱动程序。如果有法律问题,请咨 +询律师,我只是一个程序员,所以我只打算探讨技术问题(不是小看法律问题, +法律问题很实际,并且需要一直关注)。 + +既然只谈技术问题,我们就有了下面两个主题:二进制内核接口和稳定的内核源 +代码接口。这两个问题是互相关联的,让我们先解决掉二进制接口的问题。 + + +二进制内核接口 +-------------- +假如我们有一个稳定的内核源代码接口,那么自然而然的,我们就拥有了稳定的 +二进制接口,是这样的吗?错。让我们看看关于Linux内核的几点事实: + - 取决于所用的C编译器的版本,不同的内核数据结构里的结构体的对齐方 +式会有差别,代码中不同函数的表现形式也不一样(函数是不是被inline编译取 +决于编译器行为)。不同的函数的表现形式并不重要,但是数据结构内部的对齐 +方式很关键。 + - 取决于内核的配置选项,不同的选项会让内核的很多东西发生改变: + - 同一个结构体可能包含不同的成员变量 + - 有的函数可能根本不会被实现(比如编译的时候没有选择SMP支持 +,一些锁函数就会被定义成空函数)。 + - 内核使用的内存会以不同的方式对齐,这取决于不同的内核配置选 +项。 + - Linux可以在很多的不同体系结构的处理器上运行。在某个体系结构上编 +译好的二进制驱动程序,不可能在另外一个体系结构上正确的运行。 + +对于一个特定的内核,满足这些条件并不难,使用同一个C编译器和同样的内核配 +置选项来编译驱动程序模块就可以了。这对于给一个特定Linux发布的特定版本提 +供驱动程序,是完全可以满足需求的。但是如果你要给不同发布的不同版本都发 +布一个驱动程序,就需要在每个发布上用不同的内核设置参数都编译一次内核, +这简直跟噩梦一样。而且还要注意到,每个Linux发布还提供不同的Linux内核, +这些内核都针对不同的硬件类型进行了优化(有很多种不同的处理器,还有不同 +的内核设置选项)。所以每发布一次驱动程序,都需要提供很多不同版本的内核 +模块。 + +相信我,如果你真的要采取这种发布方式,一定会慢慢疯掉,我很久以前就有过 +深刻的教训... + + +稳定的内核源代码接口 +-------------------- + +如果有人不将他的内核驱动程序,放入公版内核的源代码树,而又想让驱动程序 +一直保持在最新的内核中可用,那么这个话题将会变得没完没了。 + 内核开发是持续而且快节奏的,从来都不会慢下来。内核开发人员在当前接口中 +找到bug,或者找到更好的实现方式。一旦发现这些,他们就很快会去修改当前的 +接口。修改接口意味着,函数名可能会改变,结构体可能被扩充或者删减,函数 +的参数也可能发生改变。一旦接口被修改,内核中使用这些接口的地方需要同时 +修正,这样才能保证所有的东西继续工作。 + +举一个例子,内核的USB驱动程序接口在USB子系统的整个生命周期中,至少经历 +了三次重写。这些重写解决以下问题: + - 把数据流从同步模式改成非同步模式,这个改动减少了一些驱动程序的 +复杂度,提高了所有USB驱动程序的吞吐率,这样几乎所有的USB设备都能以最大 +速率工作了。 + - 修改了USB核心代码中为USB驱动分配数据包内存的方式,所有的驱动都 +需要提供更多的参数给USB核心,以修正了很多已经被记录在案的死锁。 + +这和一些封闭源代码的操作系统形成鲜明的对比,在那些操作系统上,不得不额 +外的维护旧的USB接口。这导致了一个可能性,新的开发者依然会不小心使用旧的 +接口,以不恰当的方式编写代码,进而影响到操作系统的稳定性。 + 在上面的例子中,所有的开发者都同意这些重要的改动,在这样的情况下修改代 +价很低。如果Linux保持一个稳定的内核源代码接口,那么就得创建一个新的接口 +;旧的,有问题的接口必须一直维护,给Linux USB开发者带来额外的工作。既然 +所有的Linux USB驱动的作者都是利用自己的时间工作,那么要求他们去做毫无意 +义的免费额外工作,是不可能的。 + 安全问题对Linux来说十分重要。一个安全问题被发现,就会在短时间内得到修 +正。在很多情况下,这将导致Linux内核中的一些接口被重写,以从根本上避免安 +全问题。一旦接口被重写,所有使用这些接口的驱动程序,必须同时得到修正, +以确定安全问题已经得到修复并且不可能在未来还有同样的安全问题。如果内核 +内部接口不允许改变,那么就不可能修复这样的安全问题,也不可能确认这样的 +安全问题以后不会发生。 +开发者一直在清理内核接口。如果一个接口没有人在使用了,它就会被删除。这 +样可以确保内核尽可能的小,而且所有潜在的接口都会得到尽可能完整的测试 +(没有人使用的接口是不可能得到良好的测试的)。 + + +要做什么 +------- + +如果你写了一个Linux内核驱动,但是它还不在Linux源代码树里,作为一个开发 +者,你应该怎么做?为每个发布的每个版本提供一个二进制驱动,那简直是一个 +噩梦,要跟上永远处于变化之中的内核接口,也是一件辛苦活。 +很简单,让你的驱动进入内核源代码树(要记得我们在谈论的是以GPL许可发行 +的驱动,如果你的代码不符合GPL,那么祝你好运,你只能自己解决这个问题了, +你这个吸血鬼<把Andrew和Linus对吸血鬼的定义链接到这里>)。当你的代码加入 +公版内核源代码树之后,如果一个内核接口改变,你的驱动会直接被修改接口的 +那个人修改。保证你的驱动永远都可以编译通过,并且一直工作,你几乎不需要 +做什么事情。 + +把驱动放到内核源代码树里会有很多的好处: + - 驱动的质量会提升,而维护成本(对原始作者来说)会下降。 + - 其他人会给驱动添加新特性。 + - 其他人会找到驱动中的bug并修复。 + - 其他人会在驱动中找到性能优化的机会。 + - 当外部的接口的改变需要修改驱动程序的时候,其他人会修改驱动程序 +。 + - 不需要联系任何发行商,这个驱动会自动的随着所有的Linux发布一起发 +布。 + +和别的操作系统相比,Linux为更多不同的设备提供现成的驱动,而且能在更多不 +同体系结构的处理器上支持这些设备。这个经过考验的开发模式,必然是错不了 +的 :) + +------------- +感谢 Randy Dunlap, Andrew Morton, David Brownell, Hanna Linder, +Robert Love, and Nishanth Aravamudan 对于本文档早期版本的评审和建议。 + +英文版维护者: Greg Kroah-Hartman <greg@kroah.com> diff --git a/drivers/Kconfig b/drivers/Kconfig index 7916f4b86d2..ae01d86070b 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -84,4 +84,5 @@ source "drivers/auxdisplay/Kconfig" source "drivers/kvm/Kconfig" +source "drivers/uio/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 6d9d7fab77f..c34c8efff60 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ATA) += ata/ obj-$(CONFIG_FUSION) += message/ obj-$(CONFIG_FIREWIRE) += firewire/ obj-$(CONFIG_IEEE1394) += ieee1394/ +obj-$(CONFIG_UIO) += uio/ obj-y += cdrom/ obj-y += auxdisplay/ obj-$(CONFIG_MTD) += mtd/ diff --git a/drivers/base/core.c b/drivers/base/core.c index 0455aa78fa1..3599ab2506d 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -24,6 +24,8 @@ #include "base.h" #include "power/power.h" +extern const char *kobject_actions[]; + int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL; @@ -303,10 +305,25 @@ out: static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (memcmp(buf, "add", 3) != 0) - dev_err(dev, "uevent: unsupported action-string; this will " - "be ignored in a future kernel version"); + size_t len = count; + enum kobject_action action; + + if (len && buf[len-1] == '\n') + len--; + + for (action = 0; action < KOBJ_MAX; action++) { + if (strncmp(kobject_actions[action], buf, len) != 0) + continue; + if (kobject_actions[action][len] != '\0') + continue; + kobject_uevent(&dev->kobj, action); + goto out; + } + + dev_err(dev, "uevent: unsupported action-string; this will " + "be ignored in a future kernel version\n"); kobject_uevent(&dev->kobj, KOBJ_ADD); +out: return count; } @@ -643,6 +660,82 @@ static int setup_parent(struct device *dev, struct device *parent) return 0; } +static int device_add_class_symlinks(struct device *dev) +{ + int error; + + if (!dev->class) + return 0; + error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, + "subsystem"); + if (error) + goto out; + /* + * If this is not a "fake" compatible device, then create the + * symlink from the class to the device. + */ + if (dev->kobj.parent != &dev->class->subsys.kobj) { + error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + dev->bus_id); + if (error) + goto out_subsys; + } + /* only bus-device parents get a "device"-link */ + if (dev->parent && dev->parent->bus) { + error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, + "device"); + if (error) + goto out_busid; +#ifdef CONFIG_SYSFS_DEPRECATED + { + char * class_name = make_class_name(dev->class->name, + &dev->kobj); + if (class_name) + error = sysfs_create_link(&dev->parent->kobj, + &dev->kobj, class_name); + kfree(class_name); + if (error) + goto out_device; + } +#endif + } + return 0; + +#ifdef CONFIG_SYSFS_DEPRECATED +out_device: + if (dev->parent) + sysfs_remove_link(&dev->kobj, "device"); +#endif +out_busid: + if (dev->kobj.parent != &dev->class->subsys.kobj) + sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); +out_subsys: + sysfs_remove_link(&dev->kobj, "subsystem"); +out: + return error; +} + +static void device_remove_class_symlinks(struct device *dev) +{ + if (!dev->class) + return; + if (dev->parent) { +#ifdef CONFIG_SYSFS_DEPRECATED + char *class_name; + + class_name = make_class_name(dev->class->name, &dev->kobj); + if (class_name) { + sysfs_remove_link(&dev->parent->kobj, class_name); + kfree(class_name); + } +#endif + sysfs_remove_link(&dev->kobj, "device"); + } + if (dev->kobj.parent != &dev->class->subsys.kobj) + sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); + sysfs_remove_link(&dev->kobj, "subsystem"); +} + /** * device_add - add device to device hierarchy. * @dev: device. @@ -657,7 +750,6 @@ static int setup_parent(struct device *dev, struct device *parent) int device_add(struct device *dev) { struct device *parent = NULL; - char *class_name = NULL; struct class_interface *class_intf; int error = -EINVAL; @@ -697,27 +789,9 @@ int device_add(struct device *dev) goto ueventattrError; } - if (dev->class) { - sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, - "subsystem"); - /* If this is not a "fake" compatible device, then create the - * symlink from the class to the device. */ - if (dev->kobj.parent != &dev->class->subsys.kobj) - sysfs_create_link(&dev->class->subsys.kobj, - &dev->kobj, dev->bus_id); - if (parent) { - sysfs_create_link(&dev->kobj, &dev->parent->kobj, - "device"); -#ifdef CONFIG_SYSFS_DEPRECATED - class_name = make_class_name(dev->class->name, - &dev->kobj); - if (class_name) - sysfs_create_link(&dev->parent->kobj, - &dev->kobj, class_name); -#endif - } - } - + error = device_add_class_symlinks(dev); + if (error) + goto SymlinkError; error = device_add_attrs(dev); if (error) goto AttrsError; @@ -744,7 +818,6 @@ int device_add(struct device *dev) up(&dev->class->sem); } Done: - kfree(class_name); put_device(dev); return error; BusError: @@ -755,6 +828,8 @@ int device_add(struct device *dev) BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: + device_remove_class_symlinks(dev); + SymlinkError: if (MAJOR(dev->devt)) device_remove_file(dev, &devt_attr); @@ -1139,7 +1214,7 @@ int device_rename(struct device *dev, char *new_name) { char *old_class_name = NULL; char *new_class_name = NULL; - char *old_symlink_name = NULL; + char *old_device_name = NULL; int error; dev = get_device(dev); @@ -1153,42 +1228,49 @@ int device_rename(struct device *dev, char *new_name) old_class_name = make_class_name(dev->class->name, &dev->kobj); #endif - if (dev->class) { - old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); - if (!old_symlink_name) { - error = -ENOMEM; - goto out_free_old_class; - } - strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE); + old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL); + if (!old_device_name) { + error = -ENOMEM; + goto out; } - + strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE); strlcpy(dev->bus_id, new_name, BUS_ID_SIZE); error = kobject_rename(&dev->kobj, new_name); + if (error) { + strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE); + goto out; + } #ifdef CONFIG_SYSFS_DEPRECATED if (old_class_name) { new_class_name = make_class_name(dev->class->name, &dev->kobj); if (new_class_name) { - sysfs_create_link(&dev->parent->kobj, &dev->kobj, - new_class_name); + error = sysfs_create_link(&dev->parent->kobj, + &dev->kobj, new_class_name); + if (error) + goto out; sysfs_remove_link(&dev->parent->kobj, old_class_name); } } #endif if (dev->class) { - sysfs_remove_link(&dev->class->subsys.kobj, - old_symlink_name); - sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, - dev->bus_id); + sysfs_remove_link(&dev->class->subsys.kobj, old_device_name); + error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, + dev->bus_id); + if (error) { + /* Uh... how to unravel this if restoring can fail? */ + dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n", + __FUNCTION__, error); + } } +out: put_device(dev); kfree(new_class_name); - kfree(old_symlink_name); - out_free_old_class: kfree(old_class_name); + kfree(old_device_name); return error; } diff --git a/drivers/base/power/Makefile b/drivers/base/power/Makefile index 91f230939c1..fff17800720 100644 --- a/drivers/base/power/Makefile +++ b/drivers/base/power/Makefile @@ -1,5 +1,5 @@ obj-y := shutdown.o -obj-$(CONFIG_PM) += main.o suspend.o resume.o runtime.o sysfs.o +obj-$(CONFIG_PM) += main.o suspend.o resume.o sysfs.o obj-$(CONFIG_PM_TRACE) += trace.o ifeq ($(CONFIG_DEBUG_DRIVER),y) diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 2760f25b3ac..591a0dd5dee 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -62,11 +62,6 @@ extern int resume_device(struct device *); */ extern int suspend_device(struct device *, pm_message_t); - -/* - * runtime.c - */ - #else /* CONFIG_PM */ diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c deleted file mode 100644 index df6174d8586..00000000000 --- a/drivers/base/power/runtime.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * drivers/base/power/runtime.c - Handling dynamic device power management. - * - * Copyright (c) 2003 Patrick Mochel - * Copyright (c) 2003 Open Source Development Lab - * - */ - -#include <linux/device.h> -#include "power.h" - - -static void runtime_resume(struct device * dev) -{ - dev_dbg(dev, "resuming\n"); - if (!dev->power.power_state.event) - return; - if (!resume_device(dev)) - dev->power.power_state = PMSG_ON; -} - - -/** - * dpm_runtime_resume - Power one device back on. - * @dev: Device. - * - * Bring one device back to the on state by first powering it - * on, then restoring state. We only operate on devices that aren't - * already on. - * FIXME: We need to handle devices that are in an unknown state. - */ - -void dpm_runtime_resume(struct device * dev) -{ - mutex_lock(&dpm_mtx); - runtime_resume(dev); - mutex_unlock(&dpm_mtx); -} -EXPORT_SYMBOL(dpm_runtime_resume); - - -/** - * dpm_runtime_suspend - Put one device in low-power state. - * @dev: Device. - * @state: State to enter. - */ - -int dpm_runtime_suspend(struct device * dev, pm_message_t state) -{ - int error = 0; - - mutex_lock(&dpm_mtx); - if (dev->power.power_state.event == state.event) - goto Done; - - if (dev->power.power_state.event) - runtime_resume(dev); - - if (!(error = suspend_device(dev, state))) - dev->power.power_state = state; - Done: - mutex_unlock(&dpm_mtx); - return error; -} -EXPORT_SYMBOL(dpm_runtime_suspend); - - -#if 0 -/** - * dpm_set_power_state - Update power_state field. - * @dev: Device. - * @state: Power state device is in. - * - * This is an update mechanism for drivers to notify the core - * what power state a device is in. Device probing code may not - * always be able to tell, but we need accurate information to - * work reliably. - */ -void dpm_set_power_state(struct device * dev, pm_message_t state) -{ - mutex_lock(&dpm_mtx); - dev->power.power_state = state; - mutex_unlock(&dpm_mtx); -} -#endif /* 0 */ diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c index 2d47517dbe3..f2ed179cd69 100644 --- a/drivers/base/power/sysfs.c +++ b/drivers/base/power/sysfs.c @@ -7,69 +7,6 @@ #include "power.h" -#ifdef CONFIG_PM_SYSFS_DEPRECATED - -/** - * state - Control current power state of device - * - * show() returns the current power state of the device. '0' indicates - * the device is on. Other values (2) indicate the device is in some low - * power state. - * - * store() sets the current power state, which is an integer valued - * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early() - * methods fail this operation; those methods couldn't be called. - * Otherwise, - * - * - If the recorded dev->power.power_state.event matches the - * target value, nothing is done. - * - If the recorded event code is nonzero, the device is reactivated - * by calling bus.resume() and/or class.resume(). - * - If the target value is nonzero, the device is suspended by - * calling class.suspend() and/or bus.suspend() with event code - * PM_EVENT_SUSPEND. - * - * This mechanism is DEPRECATED and should only be used for testing. - */ - -static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf) -{ - if (dev->power.power_state.event) - return sprintf(buf, "2\n"); - else - return sprintf(buf, "0\n"); -} - -static ssize_t state_store(struct device * dev, struct device_attribute *attr, const char * buf, size_t n) -{ - pm_message_t state; - int error = -EINVAL; - - /* disallow incomplete suspend sequences */ - if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early)) - return error; - - state.event = PM_EVENT_SUSPEND; - /* Older apps expected to write "3" here - confused with PCI D3 */ - if ((n == 1) && !strcmp(buf, "3")) - error = dpm_runtime_suspend(dev, state); - - if ((n == 1) && !strcmp(buf, "2")) - error = dpm_runtime_suspend(dev, state); - - if ((n == 1) && !strcmp(buf, "0")) { - dpm_runtime_resume(dev); - error = 0; - } - - return error ? error : n; -} - -static DEVICE_ATTR(state, 0644, state_show, state_store); - - -#endif /* CONFIG_PM_SYSFS_DEPRECATED */ - /* * wakeup - Report/change current wakeup option for device * @@ -143,9 +80,6 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store); static struct attribute * power_attrs[] = { -#ifdef CONFIG_PM_SYSFS_DEPRECATED - &dev_attr_state.attr, -#endif &dev_attr_wakeup.attr, NULL, }; diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 41476abc069..db703758db9 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c @@ -224,6 +224,7 @@ ohci_update_phy_reg(struct fw_card *card, int addr, u32 val, old; reg_write(ohci, OHCI1394_PhyControl, OHCI1394_PhyControl_Read(addr)); + flush_writes(ohci); msleep(2); val = reg_read(ohci, OHCI1394_PhyControl); if ((val & OHCI1394_PhyControl_ReadDone) == 0) { @@ -586,7 +587,7 @@ static void context_stop(struct context *ctx) break; fw_notify("context_stop: still active (0x%08x)\n", reg); - msleep(1); + mdelay(1); } } diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 7c53be0387f..fc984474162 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c @@ -840,7 +840,6 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) container_of(base_orb, struct sbp2_command_orb, base); struct fw_unit *unit = orb->unit; struct fw_device *device = fw_device(unit->device.parent); - struct scatterlist *sg; int result; if (status != NULL) { @@ -876,11 +875,10 @@ complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status) dma_unmap_single(device->card->device, orb->base.request_bus, sizeof(orb->request), DMA_TO_DEVICE); - if (orb->cmd->use_sg > 0) { - sg = (struct scatterlist *)orb->cmd->request_buffer; - dma_unmap_sg(device->card->device, sg, orb->cmd->use_sg, + if (scsi_sg_count(orb->cmd) > 0) + dma_unmap_sg(device->card->device, scsi_sglist(orb->cmd), + scsi_sg_count(orb->cmd), orb->cmd->sc_data_direction); - } if (orb->page_table_bus != 0) dma_unmap_single(device->card->device, orb->page_table_bus, @@ -901,8 +899,8 @@ static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb) int sg_len, l, i, j, count; dma_addr_t sg_addr; - sg = (struct scatterlist *)orb->cmd->request_buffer; - count = dma_map_sg(device->card->device, sg, orb->cmd->use_sg, + sg = scsi_sglist(orb->cmd); + count = dma_map_sg(device->card->device, sg, scsi_sg_count(orb->cmd), orb->cmd->sc_data_direction); if (count == 0) goto fail; @@ -971,7 +969,7 @@ static int sbp2_command_orb_map_scatterlist(struct sbp2_command_orb *orb) return 0; fail_page_table: - dma_unmap_sg(device->card->device, sg, orb->cmd->use_sg, + dma_unmap_sg(device->card->device, sg, scsi_sg_count(orb->cmd), orb->cmd->sc_data_direction); fail: return -ENOMEM; @@ -1031,7 +1029,7 @@ static int sbp2_scsi_queuecommand(struct scsi_cmnd *cmd, scsi_done_fn_t done) orb->request.misc |= COMMAND_ORB_DIRECTION(SBP2_DIRECTION_TO_MEDIA); - if (cmd->use_sg && sbp2_command_orb_map_scatterlist(orb) < 0) + if (scsi_sg_count(cmd) && sbp2_command_orb_map_scatterlist(orb) < 0) goto fail_mapping; fw_memcpy_to_be32(&orb->request, &orb->request, sizeof(orb->request)); diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index 80d0121463d..3ce8e2fbe15 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c @@ -605,8 +605,10 @@ fw_send_response(struct fw_card *card, struct fw_request *request, int rcode) * check is sufficient to ensure we don't send response to * broadcast packets or posted writes. */ - if (request->ack != ACK_PENDING) + if (request->ack != ACK_PENDING) { + kfree(request); return; + } if (rcode == RCODE_COMPLETE) fw_fill_response(&request->response, request->request_header, @@ -628,11 +630,6 @@ fw_core_handle_request(struct fw_card *card, struct fw_packet *p) unsigned long flags; int tcode, destination, source; - if (p->payload_length > 2048) { - /* FIXME: send error response. */ - return; - } - if (p->ack != ACK_PENDING && p->ack != ACK_COMPLETE) return; diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index 5abed193f4a..5ceaccd1056 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h @@ -123,6 +123,10 @@ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode, size_t length, void *callback_data); +/* + * Important note: The callback must guarantee that either fw_send_response() + * or kfree() is called on the @request. + */ typedef void (*fw_address_callback_t)(struct fw_card *card, struct fw_request *request, int tcode, int destination, int source, diff --git a/drivers/infiniband/core/cm.c b/drivers/infiniband/core/cm.c index 9820c67ba47..4df269f5d9a 100644 --- a/drivers/infiniband/core/cm.c +++ b/drivers/infiniband/core/cm.c @@ -3374,7 +3374,7 @@ int ib_cm_init_qp_attr(struct ib_cm_id *cm_id, } EXPORT_SYMBOL(ib_cm_init_qp_attr); -void cm_get_ack_delay(struct cm_device *cm_dev) +static void cm_get_ack_delay(struct cm_device *cm_dev) { struct ib_device_attr attr; diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index 23af7a032a0..9ffb9987450 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -573,7 +573,7 @@ int rdma_init_qp_attr(struct rdma_cm_id *id, struct ib_qp_attr *qp_attr, break; case RDMA_TRANSPORT_IWARP: if (!id_priv->cm_id.iw) { - qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE; + qp_attr->qp_access_flags = 0; *qp_attr_mask = IB_QP_STATE | IB_QP_ACCESS_FLAGS; } else ret = iw_cm_init_qp_attr(id_priv->cm_id.iw, qp_attr, diff --git a/drivers/infiniband/hw/cxgb3/iwch_cm.c b/drivers/infiniband/hw/cxgb3/iwch_cm.c index 3b41dc0c39d..5dc68cd5621 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_cm.c +++ b/drivers/infiniband/hw/cxgb3/iwch_cm.c @@ -1914,6 +1914,7 @@ int iwch_create_listen(struct iw_cm_id *cm_id, int backlog) fail3: cxgb3_free_stid(ep->com.tdev, ep->stid); fail2: + cm_id->rem_ref(cm_id); put_ep(&ep->com); fail1: out: diff --git a/drivers/infiniband/hw/ehca/ehca_av.c b/drivers/infiniband/hw/ehca/ehca_av.c index 3cd6bf3402d..e53a97af126 100644 --- a/drivers/infiniband/hw/ehca/ehca_av.c +++ b/drivers/infiniband/hw/ehca/ehca_av.c @@ -79,7 +79,7 @@ struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) av->av.ipd = (ah_mult > 0) ? ((ehca_mult - 1) / ah_mult) : 0; } else - av->av.ipd = ehca_static_rate; + av->av.ipd = ehca_static_rate; av->av.lnh = ah_attr->ah_flags; av->av.grh.word_0 = EHCA_BMASK_SET(GRH_IPVERSION_MASK, 6); diff --git a/drivers/infiniband/hw/ehca/ehca_classes.h b/drivers/infiniband/hw/ehca/ehca_classes.h index daf823ea1ac..043e4fb23fb 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes.h +++ b/drivers/infiniband/hw/ehca/ehca_classes.h @@ -204,11 +204,11 @@ struct ehca_mr { spinlock_t mrlock; enum ehca_mr_flag flags; - u32 num_pages; /* number of MR pages */ - u32 num_4k; /* number of 4k "page" portions to form MR */ + u32 num_kpages; /* number of kernel pages */ + u32 num_hwpages; /* number of hw pages to form MR */ int acl; /* ACL (stored here for usage in reregister) */ u64 *start; /* virtual start address (stored here for */ - /* usage in reregister) */ + /* usage in reregister) */ u64 size; /* size (stored here for usage in reregister) */ u32 fmr_page_size; /* page size for FMR */ u32 fmr_max_pages; /* max pages for FMR */ @@ -217,9 +217,6 @@ struct ehca_mr { /* fw specific data */ struct ipz_mrmw_handle ipz_mr_handle; /* MR handle for h-calls */ struct h_galpas galpas; - /* data for userspace bridge */ - u32 nr_of_pages; - void *pagearray; }; struct ehca_mw { @@ -241,26 +238,29 @@ enum ehca_mr_pgi_type { struct ehca_mr_pginfo { enum ehca_mr_pgi_type type; - u64 num_pages; - u64 page_cnt; - u64 num_4k; /* number of 4k "page" portions */ - u64 page_4k_cnt; /* counter for 4k "page" portions */ - u64 next_4k; /* next 4k "page" portion in buffer/chunk/listelem */ - - /* type EHCA_MR_PGI_PHYS section */ - int num_phys_buf; - struct ib_phys_buf *phys_buf_array; - u64 next_buf; - - /* type EHCA_MR_PGI_USER section */ - struct ib_umem *region; - struct ib_umem_chunk *next_chunk; - u64 next_nmap; - - /* type EHCA_MR_PGI_FMR section */ - u64 *page_list; - u64 next_listelem; - /* next_4k also used within EHCA_MR_PGI_FMR */ + u64 num_kpages; + u64 kpage_cnt; + u64 num_hwpages; /* number of hw pages */ + u64 hwpage_cnt; /* counter for hw pages */ + u64 next_hwpage; /* next hw page in buffer/chunk/listelem */ + + union { + struct { /* type EHCA_MR_PGI_PHYS section */ + int num_phys_buf; + struct ib_phys_buf *phys_buf_array; + u64 next_buf; + } phy; + struct { /* type EHCA_MR_PGI_USER section */ + struct ib_umem *region; + struct ib_umem_chunk *next_chunk; + u64 next_nmap; + } usr; + struct { /* type EHCA_MR_PGI_FMR section */ + u64 fmr_pgsize; + u64 *page_list; + u64 next_listelem; + } fmr; + } u; }; /* output parameters for MR/FMR hipz calls */ @@ -391,6 +391,6 @@ struct ehca_alloc_qp_parms { int ehca_cq_assign_qp(struct ehca_cq *cq, struct ehca_qp *qp); int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int qp_num); -struct ehca_qp* ehca_cq_get_qp(struct ehca_cq *cq, int qp_num); +struct ehca_qp *ehca_cq_get_qp(struct ehca_cq *cq, int qp_num); #endif diff --git a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h b/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h index fb3df5c271e..1798e6466bd 100644 --- a/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h +++ b/drivers/infiniband/hw/ehca/ehca_classes_pSeries.h @@ -154,83 +154,83 @@ struct hcp_modify_qp_control_block { u32 reserved_70_127[58]; /* 70 */ }; -#define MQPCB_MASK_QKEY EHCA_BMASK_IBM(0,0) -#define MQPCB_MASK_SEND_PSN EHCA_BMASK_IBM(2,2) -#define MQPCB_MASK_RECEIVE_PSN EHCA_BMASK_IBM(3,3) -#define MQPCB_MASK_PRIM_PHYS_PORT EHCA_BMASK_IBM(4,4) -#define MQPCB_PRIM_PHYS_PORT EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_ALT_PHYS_PORT EHCA_BMASK_IBM(5,5) -#define MQPCB_MASK_PRIM_P_KEY_IDX EHCA_BMASK_IBM(6,6) -#define MQPCB_PRIM_P_KEY_IDX EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_ALT_P_KEY_IDX EHCA_BMASK_IBM(7,7) -#define MQPCB_MASK_RDMA_ATOMIC_CTRL EHCA_BMASK_IBM(8,8) -#define MQPCB_MASK_QP_STATE EHCA_BMASK_IBM(9,9) -#define MQPCB_QP_STATE EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES EHCA_BMASK_IBM(11,11) -#define MQPCB_MASK_PATH_MIGRATION_STATE EHCA_BMASK_IBM(12,12) -#define MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP EHCA_BMASK_IBM(13,13) -#define MQPCB_MASK_DEST_QP_NR EHCA_BMASK_IBM(14,14) -#define MQPCB_MASK_MIN_RNR_NAK_TIMER_FIELD EHCA_BMASK_IBM(15,15) -#define MQPCB_MASK_SERVICE_LEVEL EHCA_BMASK_IBM(16,16) -#define MQPCB_MASK_SEND_GRH_FLAG EHCA_BMASK_IBM(17,17) -#define MQPCB_MASK_RETRY_COUNT EHCA_BMASK_IBM(18,18) -#define MQPCB_MASK_TIMEOUT EHCA_BMASK_IBM(19,19) -#define MQPCB_MASK_PATH_MTU EHCA_BMASK_IBM(20,20) -#define MQPCB_PATH_MTU EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_MAX_STATIC_RATE EHCA_BMASK_IBM(21,21) -#define MQPCB_MAX_STATIC_RATE EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_DLID EHCA_BMASK_IBM(22,22) -#define MQPCB_DLID EHCA_BMASK_IBM(16,31) -#define MQPCB_MASK_RNR_RETRY_COUNT EHCA_BMASK_IBM(23,23) -#define MQPCB_RNR_RETRY_COUNT EHCA_BMASK_IBM(29,31) -#define MQPCB_MASK_SOURCE_PATH_BITS EHCA_BMASK_IBM(24,24) -#define MQPCB_SOURCE_PATH_BITS EHCA_BMASK_IBM(25,31) -#define MQPCB_MASK_TRAFFIC_CLASS EHCA_BMASK_IBM(25,25) -#define MQPCB_TRAFFIC_CLASS EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_HOP_LIMIT EHCA_BMASK_IBM(26,26) -#define MQPCB_HOP_LIMIT EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_SOURCE_GID_IDX EHCA_BMASK_IBM(27,27) -#define MQPCB_SOURCE_GID_IDX EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_FLOW_LABEL EHCA_BMASK_IBM(28,28) -#define MQPCB_FLOW_LABEL EHCA_BMASK_IBM(12,31) -#define MQPCB_MASK_DEST_GID EHCA_BMASK_IBM(30,30) -#define MQPCB_MASK_SERVICE_LEVEL_AL EHCA_BMASK_IBM(31,31) -#define MQPCB_SERVICE_LEVEL_AL EHCA_BMASK_IBM(28,31) -#define MQPCB_MASK_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(32,32) -#define MQPCB_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(31,31) -#define MQPCB_MASK_RETRY_COUNT_AL EHCA_BMASK_IBM(33,33) -#define MQPCB_RETRY_COUNT_AL EHCA_BMASK_IBM(29,31) -#define MQPCB_MASK_TIMEOUT_AL EHCA_BMASK_IBM(34,34) -#define MQPCB_TIMEOUT_AL EHCA_BMASK_IBM(27,31) -#define MQPCB_MASK_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(35,35) -#define MQPCB_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_DLID_AL EHCA_BMASK_IBM(36,36) -#define MQPCB_DLID_AL EHCA_BMASK_IBM(16,31) -#define MQPCB_MASK_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(37,37) -#define MQPCB_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(29,31) -#define MQPCB_MASK_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(38,38) -#define MQPCB_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(25,31) -#define MQPCB_MASK_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(39,39) -#define MQPCB_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_HOP_LIMIT_AL EHCA_BMASK_IBM(40,40) -#define MQPCB_HOP_LIMIT_AL EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(41,41) -#define MQPCB_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(24,31) -#define MQPCB_MASK_FLOW_LABEL_AL EHCA_BMASK_IBM(42,42) -#define MQPCB_FLOW_LABEL_AL EHCA_BMASK_IBM(12,31) -#define MQPCB_MASK_DEST_GID_AL EHCA_BMASK_IBM(44,44) -#define MQPCB_MASK_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(45,45) -#define MQPCB_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(16,31) -#define MQPCB_MASK_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(46,46) -#define MQPCB_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(16,31) -#define MQPCB_MASK_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(47,47) -#define MQPCB_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(31,31) -#define MQPCB_QP_NUMBER EHCA_BMASK_IBM(8,31) -#define MQPCB_MASK_QP_ENABLE EHCA_BMASK_IBM(48,48) -#define MQPCB_QP_ENABLE EHCA_BMASK_IBM(31,31) -#define MQPCB_MASK_CURR_SRQ_LIMIT EHCA_BMASK_IBM(49,49) -#define MQPCB_CURR_SRQ_LIMIT EHCA_BMASK_IBM(16,31) -#define MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG EHCA_BMASK_IBM(50,50) -#define MQPCB_MASK_SHARED_RQ_HNDL EHCA_BMASK_IBM(51,51) +#define MQPCB_MASK_QKEY EHCA_BMASK_IBM( 0, 0) +#define MQPCB_MASK_SEND_PSN EHCA_BMASK_IBM( 2, 2) +#define MQPCB_MASK_RECEIVE_PSN EHCA_BMASK_IBM( 3, 3) +#define MQPCB_MASK_PRIM_PHYS_PORT EHCA_BMASK_IBM( 4, 4) +#define MQPCB_PRIM_PHYS_PORT EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_ALT_PHYS_PORT EHCA_BMASK_IBM( 5, 5) +#define MQPCB_MASK_PRIM_P_KEY_IDX EHCA_BMASK_IBM( 6, 6) +#define MQPCB_PRIM_P_KEY_IDX EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_ALT_P_KEY_IDX EHCA_BMASK_IBM( 7, 7) +#define MQPCB_MASK_RDMA_ATOMIC_CTRL EHCA_BMASK_IBM( 8, 8) +#define MQPCB_MASK_QP_STATE EHCA_BMASK_IBM( 9, 9) +#define MQPCB_QP_STATE EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_RDMA_NR_ATOMIC_RESP_RES EHCA_BMASK_IBM(11, 11) +#define MQPCB_MASK_PATH_MIGRATION_STATE EHCA_BMASK_IBM(12, 12) +#define MQPCB_MASK_RDMA_ATOMIC_OUTST_DEST_QP EHCA_BMASK_IBM(13, 13) +#define MQPCB_MASK_DEST_QP_NR EHCA_BMASK_IBM(14, 14) +#define MQPCB_MASK_MIN_RNR_NAK_TIMER_FIELD EHCA_BMASK_IBM(15, 15) +#define MQPCB_MASK_SERVICE_LEVEL EHCA_BMASK_IBM(16, 16) +#define MQPCB_MASK_SEND_GRH_FLAG EHCA_BMASK_IBM(17, 17) +#define MQPCB_MASK_RETRY_COUNT EHCA_BMASK_IBM(18, 18) +#define MQPCB_MASK_TIMEOUT EHCA_BMASK_IBM(19, 19) +#define MQPCB_MASK_PATH_MTU EHCA_BMASK_IBM(20, 20) +#define MQPCB_PATH_MTU EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_MAX_STATIC_RATE EHCA_BMASK_IBM(21, 21) +#define MQPCB_MAX_STATIC_RATE EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_DLID EHCA_BMASK_IBM(22, 22) +#define MQPCB_DLID EHCA_BMASK_IBM(16, 31) +#define MQPCB_MASK_RNR_RETRY_COUNT EHCA_BMASK_IBM(23, 23) +#define MQPCB_RNR_RETRY_COUNT EHCA_BMASK_IBM(29, 31) +#define MQPCB_MASK_SOURCE_PATH_BITS EHCA_BMASK_IBM(24, 24) +#define MQPCB_SOURCE_PATH_BITS EHCA_BMASK_IBM(25, 31) +#define MQPCB_MASK_TRAFFIC_CLASS EHCA_BMASK_IBM(25, 25) +#define MQPCB_TRAFFIC_CLASS EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_HOP_LIMIT EHCA_BMASK_IBM(26, 26) +#define MQPCB_HOP_LIMIT EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_SOURCE_GID_IDX EHCA_BMASK_IBM(27, 27) +#define MQPCB_SOURCE_GID_IDX EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_FLOW_LABEL EHCA_BMASK_IBM(28, 28) +#define MQPCB_FLOW_LABEL EHCA_BMASK_IBM(12, 31) +#define MQPCB_MASK_DEST_GID EHCA_BMASK_IBM(30, 30) +#define MQPCB_MASK_SERVICE_LEVEL_AL EHCA_BMASK_IBM(31, 31) +#define MQPCB_SERVICE_LEVEL_AL EHCA_BMASK_IBM(28, 31) +#define MQPCB_MASK_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(32, 32) +#define MQPCB_SEND_GRH_FLAG_AL EHCA_BMASK_IBM(31, 31) +#define MQPCB_MASK_RETRY_COUNT_AL EHCA_BMASK_IBM(33, 33) +#define MQPCB_RETRY_COUNT_AL EHCA_BMASK_IBM(29, 31) +#define MQPCB_MASK_TIMEOUT_AL EHCA_BMASK_IBM(34, 34) +#define MQPCB_TIMEOUT_AL EHCA_BMASK_IBM(27, 31) +#define MQPCB_MASK_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(35, 35) +#define MQPCB_MAX_STATIC_RATE_AL EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_DLID_AL EHCA_BMASK_IBM(36, 36) +#define MQPCB_DLID_AL EHCA_BMASK_IBM(16, 31) +#define MQPCB_MASK_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(37, 37) +#define MQPCB_RNR_RETRY_COUNT_AL EHCA_BMASK_IBM(29, 31) +#define MQPCB_MASK_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(38, 38) +#define MQPCB_SOURCE_PATH_BITS_AL EHCA_BMASK_IBM(25, 31) +#define MQPCB_MASK_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(39, 39) +#define MQPCB_TRAFFIC_CLASS_AL EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_HOP_LIMIT_AL EHCA_BMASK_IBM(40, 40) +#define MQPCB_HOP_LIMIT_AL EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(41, 41) +#define MQPCB_SOURCE_GID_IDX_AL EHCA_BMASK_IBM(24, 31) +#define MQPCB_MASK_FLOW_LABEL_AL EHCA_BMASK_IBM(42, 42) +#define MQPCB_FLOW_LABEL_AL EHCA_BMASK_IBM(12, 31) +#define MQPCB_MASK_DEST_GID_AL EHCA_BMASK_IBM(44, 44) +#define MQPCB_MASK_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(45, 45) +#define MQPCB_MAX_NR_OUTST_SEND_WR EHCA_BMASK_IBM(16, 31) +#define MQPCB_MASK_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(46, 46) +#define MQPCB_MAX_NR_OUTST_RECV_WR EHCA_BMASK_IBM(16, 31) +#define MQPCB_MASK_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(47, 47) +#define MQPCB_DISABLE_ETE_CREDIT_CHECK EHCA_BMASK_IBM(31, 31) +#define MQPCB_QP_NUMBER EHCA_BMASK_IBM( 8, 31) +#define MQPCB_MASK_QP_ENABLE EHCA_BMASK_IBM(48, 48) +#define MQPCB_QP_ENABLE EHCA_BMASK_IBM(31, 31) +#define MQPCB_MASK_CURR_SRQ_LIMIT EHCA_BMASK_IBM(49, 49) +#define MQPCB_CURR_SRQ_LIMIT EHCA_BMASK_IBM(16, 31) +#define MQPCB_MASK_QP_AFF_ASYN_EV_LOG_REG EHCA_BMASK_IBM(50, 50) +#define MQPCB_MASK_SHARED_RQ_HNDL EHCA_BMASK_IBM(51, 51) #endif /* __EHCA_CLASSES_PSERIES_H__ */ diff --git a/drivers/infiniband/hw/ehca/ehca_cq.c b/drivers/infiniband/hw/ehca/ehca_cq.c index 01d4a148bd7..9e87883b561 100644 --- a/drivers/infiniband/hw/ehca/ehca_cq.c +++ b/drivers/infiniband/hw/ehca/ehca_cq.c @@ -97,7 +97,7 @@ int ehca_cq_unassign_qp(struct ehca_cq *cq, unsigned int real_qp_num) return ret; } -struct ehca_qp* ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num) +struct ehca_qp *ehca_cq_get_qp(struct ehca_cq *cq, int real_qp_num) { struct ehca_qp *ret = NULL; unsigned int key = real_qp_num & (QP_HASHTAB_LEN-1); diff --git a/drivers/infiniband/hw/ehca/ehca_eq.c b/drivers/infiniband/hw/ehca/ehca_eq.c index 4961eb88827..4825975f88c 100644 --- a/drivers/infiniband/hw/ehca/ehca_eq.c +++ b/drivers/infiniband/hw/ehca/ehca_eq.c @@ -96,7 +96,8 @@ int ehca_create_eq(struct ehca_shca *shca, for (i = 0; i < nr_pages; i++) { u64 rpage; - if (!(vpage = ipz_qpageit_get_inc(&eq->ipz_queue))) { + vpage = ipz_qpageit_get_inc(&eq->ipz_queue); + if (!vpage) { ret = H_RESOURCE; goto create_eq_exit2; } diff --git a/drivers/infiniband/hw/ehca/ehca_hca.c b/drivers/infiniband/hw/ehca/ehca_hca.c index bbd3c6a5822..fc19ef9fd96 100644 --- a/drivers/infiniband/hw/ehca/ehca_hca.c +++ b/drivers/infiniband/hw/ehca/ehca_hca.c @@ -127,6 +127,7 @@ int ehca_query_port(struct ib_device *ibdev, u8 port, struct ib_port_attr *props) { int ret = 0; + u64 h_ret; struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); struct hipz_query_port *rblock; @@ -137,7 +138,8 @@ int ehca_query_port(struct ib_device *ibdev, return -ENOMEM; } - if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + h_ret = hipz_h_query_port(shca->ipz_hca_handle, port, rblock); + if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "Can't query port properties"); ret = -EINVAL; goto query_port1; @@ -197,6 +199,7 @@ int ehca_query_sma_attr(struct ehca_shca *shca, u8 port, struct ehca_sma_attr *attr) { int ret = 0; + u64 h_ret; struct hipz_query_port *rblock; rblock = ehca_alloc_fw_ctrlblock(GFP_ATOMIC); @@ -205,7 +208,8 @@ int ehca_query_sma_attr(struct ehca_shca *shca, return -ENOMEM; } - if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + h_ret = hipz_h_query_port(shca->ipz_hca_handle, port, rblock); + if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "Can't query port properties"); ret = -EINVAL; goto query_sma_attr1; @@ -230,9 +234,11 @@ query_sma_attr1: int ehca_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) { int ret = 0; - struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); + u64 h_ret; + struct ehca_shca *shca; struct hipz_query_port *rblock; + shca = container_of(ibdev, struct ehca_shca, ib_device); if (index > 16) { ehca_err(&shca->ib_device, "Invalid index: %x.", index); return -EINVAL; @@ -244,7 +250,8 @@ int ehca_query_pkey(struct ib_device *ibdev, u8 port, u16 index, u16 *pkey) return -ENOMEM; } - if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + h_ret = hipz_h_query_port(shca->ipz_hca_handle, port, rblock); + if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "Can't query port properties"); ret = -EINVAL; goto query_pkey1; @@ -262,6 +269,7 @@ int ehca_query_gid(struct ib_device *ibdev, u8 port, int index, union ib_gid *gid) { int ret = 0; + u64 h_ret; struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); struct hipz_query_port *rblock; @@ -277,7 +285,8 @@ int ehca_query_gid(struct ib_device *ibdev, u8 port, return -ENOMEM; } - if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + h_ret = hipz_h_query_port(shca->ipz_hca_handle, port, rblock); + if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "Can't query port properties"); ret = -EINVAL; goto query_gid1; @@ -302,11 +311,12 @@ int ehca_modify_port(struct ib_device *ibdev, struct ib_port_modify *props) { int ret = 0; - struct ehca_shca *shca = container_of(ibdev, struct ehca_shca, ib_device); + struct ehca_shca *shca; struct hipz_query_port *rblock; u32 cap; u64 hret; + shca = container_of(ibdev, struct ehca_shca, ib_device); if ((props->set_port_cap_mask | props->clr_port_cap_mask) & ~allowed_port_caps) { ehca_err(&shca->ib_device, "Non-changeable bits set in masks " @@ -325,7 +335,8 @@ int ehca_modify_port(struct ib_device *ibdev, goto modify_port1; } - if (hipz_h_query_port(shca->ipz_hca_handle, port, rblock) != H_SUCCESS) { + hret = hipz_h_query_port(shca->ipz_hca_handle, port, rblock); + if (hret != H_SUCCESS) { ehca_err(&shca->ib_device, "Can't query port properties"); ret = -EINVAL; goto modify_port2; @@ -337,7 +348,8 @@ int ehca_modify_port(struct ib_device *ibdev, hret = hipz_h_modify_port(shca->ipz_hca_handle, port, cap, props->init_type, port_modify_mask); if (hret != H_SUCCESS) { - ehca_err(&shca->ib_device, "Modify port failed hret=%lx", hret); + ehca_err(&shca->ib_device, "Modify port failed hret=%lx", + hret); ret = -EINVAL; } diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index 96eba383075..4fb01fcb63a 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -49,26 +49,26 @@ #include "hipz_fns.h" #include "ipz_pt_fn.h" -#define EQE_COMPLETION_EVENT EHCA_BMASK_IBM(1,1) -#define EQE_CQ_QP_NUMBER EHCA_BMASK_IBM(8,31) -#define EQE_EE_IDENTIFIER EHCA_BMASK_IBM(2,7) -#define EQE_CQ_NUMBER EHCA_BMASK_IBM(8,31) -#define EQE_QP_NUMBER EHCA_BMASK_IBM(8,31) -#define EQE_QP_TOKEN EHCA_BMASK_IBM(32,63) -#define EQE_CQ_TOKEN EHCA_BMASK_IBM(32,63) - -#define NEQE_COMPLETION_EVENT EHCA_BMASK_IBM(1,1) -#define NEQE_EVENT_CODE EHCA_BMASK_IBM(2,7) -#define NEQE_PORT_NUMBER EHCA_BMASK_IBM(8,15) -#define NEQE_PORT_AVAILABILITY EHCA_BMASK_IBM(16,16) -#define NEQE_DISRUPTIVE EHCA_BMASK_IBM(16,16) - -#define ERROR_DATA_LENGTH EHCA_BMASK_IBM(52,63) -#define ERROR_DATA_TYPE EHCA_BMASK_IBM(0,7) +#define EQE_COMPLETION_EVENT EHCA_BMASK_IBM( 1, 1) +#define EQE_CQ_QP_NUMBER EHCA_BMASK_IBM( 8, 31) +#define EQE_EE_IDENTIFIER EHCA_BMASK_IBM( 2, 7) +#define EQE_CQ_NUMBER EHCA_BMASK_IBM( 8, 31) +#define EQE_QP_NUMBER EHCA_BMASK_IBM( 8, 31) +#define EQE_QP_TOKEN EHCA_BMASK_IBM(32, 63) +#define EQE_CQ_TOKEN EHCA_BMASK_IBM(32, 63) + +#define NEQE_COMPLETION_EVENT EHCA_BMASK_IBM( 1, 1) +#define NEQE_EVENT_CODE EHCA_BMASK_IBM( 2, 7) +#define NEQE_PORT_NUMBER EHCA_BMASK_IBM( 8, 15) +#define NEQE_PORT_AVAILABILITY EHCA_BMASK_IBM(16, 16) +#define NEQE_DISRUPTIVE EHCA_BMASK_IBM(16, 16) + +#define ERROR_DATA_LENGTH EHCA_BMASK_IBM(52, 63) +#define ERROR_DATA_TYPE EHCA_BMASK_IBM( 0, 7) static void queue_comp_task(struct ehca_cq *__cq); -static struct ehca_comp_pool* pool; +static struct ehca_comp_pool *pool; #ifdef CONFIG_HOTPLUG_CPU static struct notifier_block comp_pool_callback_nb; #endif @@ -85,8 +85,8 @@ static inline void comp_event_callback(struct ehca_cq *cq) return; } -static void print_error_data(struct ehca_shca * shca, void* data, - u64* rblock, int length) +static void print_error_data(struct ehca_shca *shca, void *data, + u64 *rblock, int length) { u64 type = EHCA_BMASK_GET(ERROR_DATA_TYPE, rblock[2]); u64 resource = rblock[1]; @@ -94,7 +94,7 @@ static void print_error_data(struct ehca_shca * shca, void* data, switch (type) { case 0x1: /* Queue Pair */ { - struct ehca_qp *qp = (struct ehca_qp*)data; + struct ehca_qp *qp = (struct ehca_qp *)data; /* only print error data if AER is set */ if (rblock[6] == 0) @@ -107,7 +107,7 @@ static void print_error_data(struct ehca_shca * shca, void* data, } case 0x4: /* Completion Queue */ { - struct ehca_cq *cq = (struct ehca_cq*)data; + struct ehca_cq *cq = (struct ehca_cq *)data; ehca_err(&shca->ib_device, "CQ 0x%x (resource=%lx) has errors.", @@ -572,7 +572,7 @@ void ehca_tasklet_eq(unsigned long data) ehca_process_eq((struct ehca_shca*)data, 1); } -static inline int find_next_online_cpu(struct ehca_comp_pool* pool) +static inline int find_next_online_cpu(struct ehca_comp_pool *pool) { int cpu; unsigned long flags; @@ -636,7 +636,7 @@ static void queue_comp_task(struct ehca_cq *__cq) __queue_comp_task(__cq, cct); } -static void run_comp_task(struct ehca_cpu_comp_task* cct) +static void run_comp_task(struct ehca_cpu_comp_task *cct) { struct ehca_cq *cq; unsigned long flags; @@ -666,12 +666,12 @@ static void run_comp_task(struct ehca_cpu_comp_task* cct) static int comp_task(void *__cct) { - struct ehca_cpu_comp_task* cct = __cct; + struct ehca_cpu_comp_task *cct = __cct; int cql_empty; DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_INTERRUPTIBLE); - while(!kthread_should_stop()) { + while (!kthread_should_stop()) { add_wait_queue(&cct->wait_queue, &wait); spin_lock_irq(&cct->task_lock); @@ -745,7 +745,7 @@ static void take_over_work(struct ehca_comp_pool *pool, list_splice_init(&cct->cq_list, &list); - while(!list_empty(&list)) { + while (!list_empty(&list)) { cq = list_entry(cct->cq_list.next, struct ehca_cq, entry); list_del(&cq->entry); @@ -768,7 +768,7 @@ static int comp_pool_callback(struct notifier_block *nfb, case CPU_UP_PREPARE: case CPU_UP_PREPARE_FROZEN: ehca_gen_dbg("CPU: %x (CPU_PREPARE)", cpu); - if(!create_comp_task(pool, cpu)) { + if (!create_comp_task(pool, cpu)) { ehca_gen_err("Can't create comp_task for cpu: %x", cpu); return NOTIFY_BAD; } @@ -838,7 +838,7 @@ int ehca_create_comp_pool(void) #ifdef CONFIG_HOTPLUG_CPU comp_pool_callback_nb.notifier_call = comp_pool_callback; - comp_pool_callback_nb.priority =0; + comp_pool_callback_nb.priority = 0; register_cpu_notifier(&comp_pool_callback_nb); #endif diff --git a/drivers/infiniband/hw/ehca/ehca_iverbs.h b/drivers/infiniband/hw/ehca/ehca_iverbs.h index 77aeca6a2c2..dce503bb7d6 100644 --- a/drivers/infiniband/hw/ehca/ehca_iverbs.h +++ b/drivers/infiniband/hw/ehca/ehca_iverbs.h @@ -81,8 +81,9 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, int num_phys_buf, int mr_access_flags, u64 *iova_start); -struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, - int mr_access_flags, struct ib_udata *udata); +struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int mr_access_flags, + struct ib_udata *udata); int ehca_rereg_phys_mr(struct ib_mr *mr, int mr_rereg_mask, @@ -192,7 +193,7 @@ void ehca_poll_eqs(unsigned long data); void *ehca_alloc_fw_ctrlblock(gfp_t flags); void ehca_free_fw_ctrlblock(void *ptr); #else -#define ehca_alloc_fw_ctrlblock(flags) ((void *) get_zeroed_page(flags)) +#define ehca_alloc_fw_ctrlblock(flags) ((void *)get_zeroed_page(flags)) #define ehca_free_fw_ctrlblock(ptr) free_page((unsigned long)(ptr)) #endif diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c index 28ba2dd2421..36377c6db3d 100644 --- a/drivers/infiniband/hw/ehca/ehca_main.c +++ b/drivers/infiniband/hw/ehca/ehca_main.c @@ -107,7 +107,7 @@ static DEFINE_SPINLOCK(shca_list_lock); static struct timer_list poll_eqs_timer; #ifdef CONFIG_PPC_64K_PAGES -static struct kmem_cache *ctblk_cache = NULL; +static struct kmem_cache *ctblk_cache; void *ehca_alloc_fw_ctrlblock(gfp_t flags) { @@ -200,8 +200,8 @@ static void ehca_destroy_slab_caches(void) #endif } -#define EHCA_HCAAVER EHCA_BMASK_IBM(32,39) -#define EHCA_REVID EHCA_BMASK_IBM(40,63) +#define EHCA_HCAAVER EHCA_BMASK_IBM(32, 39) +#define EHCA_REVID EHCA_BMASK_IBM(40, 63) static struct cap_descr { u64 mask; @@ -263,22 +263,27 @@ int ehca_sense_attributes(struct ehca_shca *shca) ehca_gen_dbg(" ... hardware version=%x:%x", hcaaver, revid); - if ((hcaaver == 1) && (revid == 0)) - shca->hw_level = 0x11; - else if ((hcaaver == 1) && (revid == 1)) - shca->hw_level = 0x12; - else if ((hcaaver == 1) && (revid == 2)) - shca->hw_level = 0x13; - else if ((hcaaver == 2) && (revid == 0)) - shca->hw_level = 0x21; - else if ((hcaaver == 2) && (revid == 0x10)) - shca->hw_level = 0x22; - else { + if (hcaaver == 1) { + if (revid <= 3) + shca->hw_level = 0x10 | (revid + 1); + else + shca->hw_level = 0x14; + } else if (hcaaver == 2) { + if (revid == 0) + shca->hw_level = 0x21; + else if (revid == 0x10) + shca->hw_level = 0x22; + else if (revid == 0x20 || revid == 0x21) + shca->hw_level = 0x23; + } + + if (!shca->hw_level) { ehca_gen_warn("unknown hardware version" " - assuming default level"); shca->hw_level = 0x22; } - } + } else + shca->hw_level = ehca_hw_level; ehca_gen_dbg(" ... hardware level=%x", shca->hw_level); shca->sport[0].rate = IB_RATE_30_GBPS; @@ -290,7 +295,7 @@ int ehca_sense_attributes(struct ehca_shca *shca) if (EHCA_BMASK_GET(hca_cap_descr[i].mask, shca->hca_cap)) ehca_gen_dbg(" %s", hca_cap_descr[i].descr); - port = (struct hipz_query_port *) rblock; + port = (struct hipz_query_port *)rblock; h_ret = hipz_h_query_port(shca->ipz_hca_handle, 1, port); if (h_ret != H_SUCCESS) { ehca_gen_err("Cannot query port properties. h_ret=%lx", @@ -439,7 +444,7 @@ static int ehca_create_aqp1(struct ehca_shca *shca, u32 port) return -EPERM; } - ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void*)(-1), 10, 0); + ibcq = ib_create_cq(&shca->ib_device, NULL, NULL, (void *)(-1), 10, 0); if (IS_ERR(ibcq)) { ehca_err(&shca->ib_device, "Cannot create AQP1 CQ."); return PTR_ERR(ibcq); @@ -666,7 +671,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev, } /* create internal protection domain */ - ibpd = ehca_alloc_pd(&shca->ib_device, (void*)(-1), NULL); + ibpd = ehca_alloc_pd(&shca->ib_device, (void *)(-1), NULL); if (IS_ERR(ibpd)) { ehca_err(&shca->ib_device, "Cannot create internal PD."); ret = PTR_ERR(ibpd); @@ -863,18 +868,21 @@ int __init ehca_module_init(void) printk(KERN_INFO "eHCA Infiniband Device Driver " "(Rel.: SVNEHCA_0023)\n"); - if ((ret = ehca_create_comp_pool())) { + ret = ehca_create_comp_pool(); + if (ret) { ehca_gen_err("Cannot create comp pool."); return ret; } - if ((ret = ehca_create_slab_caches())) { + ret = ehca_create_slab_caches(); + if (ret) { ehca_gen_err("Cannot create SLAB caches"); ret = -ENOMEM; goto module_init1; } - if ((ret = ibmebus_register_driver(&ehca_driver))) { + ret = ibmebus_register_driver(&ehca_driver); + if (ret) { ehca_gen_err("Cannot register eHCA device driver"); ret = -EINVAL; goto module_init2; diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.c b/drivers/infiniband/hw/ehca/ehca_mrmw.c index add79bd44e3..6262c5462d5 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.c +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.c @@ -48,6 +48,11 @@ #include "hcp_if.h" #include "hipz_hw.h" +#define NUM_CHUNKS(length, chunk_size) \ + (((length) + (chunk_size - 1)) / (chunk_size)) +/* max number of rpages (per hcall register_rpages) */ +#define MAX_RPAGES 512 + static struct kmem_cache *mr_cache; static struct kmem_cache *mw_cache; @@ -56,9 +61,9 @@ static struct ehca_mr *ehca_mr_new(void) struct ehca_mr *me; me = kmem_cache_zalloc(mr_cache, GFP_KERNEL); - if (me) { + if (me) spin_lock_init(&me->mrlock); - } else + else ehca_gen_err("alloc failed"); return me; @@ -74,9 +79,9 @@ static struct ehca_mw *ehca_mw_new(void) struct ehca_mw *me; me = kmem_cache_zalloc(mw_cache, GFP_KERNEL); - if (me) { + if (me) spin_lock_init(&me->mwlock); - } else + else ehca_gen_err("alloc failed"); return me; @@ -106,11 +111,12 @@ struct ib_mr *ehca_get_dma_mr(struct ib_pd *pd, int mr_access_flags) goto get_dma_mr_exit0; } - ret = ehca_reg_maxmr(shca, e_maxmr, (u64*)KERNELBASE, + ret = ehca_reg_maxmr(shca, e_maxmr, (u64 *)KERNELBASE, mr_access_flags, e_pd, &e_maxmr->ib.ib_mr.lkey, &e_maxmr->ib.ib_mr.rkey); if (ret) { + ehca_mr_delete(e_maxmr); ib_mr = ERR_PTR(ret); goto get_dma_mr_exit0; } @@ -144,9 +150,6 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, struct ehca_pd *e_pd = container_of(pd, struct ehca_pd, ib_pd); u64 size; - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; - u32 num_pages_mr; - u32 num_pages_4k; /* 4k portion "pages" */ if ((num_phys_buf <= 0) || !phys_buf_array) { ehca_err(pd->device, "bad input values: num_phys_buf=%x " @@ -190,12 +193,6 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, goto reg_phys_mr_exit0; } - /* determine number of MR pages */ - num_pages_mr = ((((u64)iova_start % PAGE_SIZE) + size + - PAGE_SIZE - 1) / PAGE_SIZE); - num_pages_4k = ((((u64)iova_start % EHCA_PAGESIZE) + size + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE); - /* register MR on HCA */ if (ehca_mr_is_maxmr(size, iova_start)) { e_mr->flags |= EHCA_MR_FLAG_MAXMR; @@ -207,13 +204,22 @@ struct ib_mr *ehca_reg_phys_mr(struct ib_pd *pd, goto reg_phys_mr_exit1; } } else { - pginfo.type = EHCA_MR_PGI_PHYS; - pginfo.num_pages = num_pages_mr; - pginfo.num_4k = num_pages_4k; - pginfo.num_phys_buf = num_phys_buf; - pginfo.phys_buf_array = phys_buf_array; - pginfo.next_4k = (((u64)iova_start & ~PAGE_MASK) / - EHCA_PAGESIZE); + struct ehca_mr_pginfo pginfo; + u32 num_kpages; + u32 num_hwpages; + + num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size, + PAGE_SIZE); + num_hwpages = NUM_CHUNKS(((u64)iova_start % EHCA_PAGESIZE) + + size, EHCA_PAGESIZE); + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_PHYS; + pginfo.num_kpages = num_kpages; + pginfo.num_hwpages = num_hwpages; + pginfo.u.phy.num_phys_buf = num_phys_buf; + pginfo.u.phy.phys_buf_array = phys_buf_array; + pginfo.next_hwpage = (((u64)iova_start & ~PAGE_MASK) / + EHCA_PAGESIZE); ret = ehca_reg_mr(shca, e_mr, iova_start, size, mr_access_flags, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, @@ -240,18 +246,19 @@ reg_phys_mr_exit0: /*----------------------------------------------------------------------*/ -struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt, - int mr_access_flags, struct ib_udata *udata) +struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, + u64 virt, int mr_access_flags, + struct ib_udata *udata) { struct ib_mr *ib_mr; struct ehca_mr *e_mr; struct ehca_shca *shca = container_of(pd->device, struct ehca_shca, ib_device); struct ehca_pd *e_pd = container_of(pd, struct ehca_pd, ib_pd); - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; + struct ehca_mr_pginfo pginfo; int ret; - u32 num_pages_mr; - u32 num_pages_4k; /* 4k portion "pages" */ + u32 num_kpages; + u32 num_hwpages; if (!pd) { ehca_gen_err("bad pd=%p", pd); @@ -289,7 +296,7 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt e_mr->umem = ib_umem_get(pd->uobject->context, start, length, mr_access_flags); if (IS_ERR(e_mr->umem)) { - ib_mr = (void *) e_mr->umem; + ib_mr = (void *)e_mr->umem; goto reg_user_mr_exit1; } @@ -301,23 +308,24 @@ struct ib_mr *ehca_reg_user_mr(struct ib_pd *pd, u64 start, u64 length, u64 virt } /* determine number of MR pages */ - num_pages_mr = (((virt % PAGE_SIZE) + length + PAGE_SIZE - 1) / - PAGE_SIZE); - num_pages_4k = (((virt % EHCA_PAGESIZE) + length + EHCA_PAGESIZE - 1) / - EHCA_PAGESIZE); + num_kpages = NUM_CHUNKS((virt % PAGE_SIZE) + length, PAGE_SIZE); + num_hwpages = NUM_CHUNKS((virt % EHCA_PAGESIZE) + length, + EHCA_PAGESIZE); /* register MR on HCA */ - pginfo.type = EHCA_MR_PGI_USER; - pginfo.num_pages = num_pages_mr; - pginfo.num_4k = num_pages_4k; - pginfo.region = e_mr->umem; - pginfo.next_4k = e_mr->umem->offset / EHCA_PAGESIZE; - pginfo.next_chunk = list_prepare_entry(pginfo.next_chunk, - (&e_mr->umem->chunk_list), - list); - - ret = ehca_reg_mr(shca, e_mr, (u64*) virt, length, mr_access_flags, e_pd, - &pginfo, &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey); + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_USER; + pginfo.num_kpages = num_kpages; + pginfo.num_hwpages = num_hwpages; + pginfo.u.usr.region = e_mr->umem; + pginfo.next_hwpage = e_mr->umem->offset / EHCA_PAGESIZE; + pginfo.u.usr.next_chunk = list_prepare_entry(pginfo.u.usr.next_chunk, + (&e_mr->umem->chunk_list), + list); + + ret = ehca_reg_mr(shca, e_mr, (u64 *)virt, length, mr_access_flags, + e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, + &e_mr->ib.ib_mr.rkey); if (ret) { ib_mr = ERR_PTR(ret); goto reg_user_mr_exit2; @@ -360,9 +368,9 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, struct ehca_pd *new_pd; u32 tmp_lkey, tmp_rkey; unsigned long sl_flags; - u32 num_pages_mr = 0; - u32 num_pages_4k = 0; /* 4k portion "pages" */ - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; + u32 num_kpages = 0; + u32 num_hwpages = 0; + struct ehca_mr_pginfo pginfo; u32 cur_pid = current->tgid; if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && @@ -414,7 +422,7 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, goto rereg_phys_mr_exit0; } if (!phys_buf_array || num_phys_buf <= 0) { - ehca_err(mr->device, "bad input values: mr_rereg_mask=%x" + ehca_err(mr->device, "bad input values mr_rereg_mask=%x" " phys_buf_array=%p num_phys_buf=%x", mr_rereg_mask, phys_buf_array, num_phys_buf); ret = -EINVAL; @@ -438,10 +446,10 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, /* set requested values dependent on rereg request */ spin_lock_irqsave(&e_mr->mrlock, sl_flags); - new_start = e_mr->start; /* new == old address */ - new_size = e_mr->size; /* new == old length */ - new_acl = e_mr->acl; /* new == old access control */ - new_pd = container_of(mr->pd,struct ehca_pd,ib_pd); /*new == old PD*/ + new_start = e_mr->start; + new_size = e_mr->size; + new_acl = e_mr->acl; + new_pd = container_of(mr->pd, struct ehca_pd, ib_pd); if (mr_rereg_mask & IB_MR_REREG_TRANS) { new_start = iova_start; /* change address */ @@ -458,17 +466,18 @@ int ehca_rereg_phys_mr(struct ib_mr *mr, ret = -EINVAL; goto rereg_phys_mr_exit1; } - num_pages_mr = ((((u64)new_start % PAGE_SIZE) + new_size + - PAGE_SIZE - 1) / PAGE_SIZE); - num_pages_4k = ((((u64)new_start % EHCA_PAGESIZE) + new_size + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE); - pginfo.type = EHCA_MR_PGI_PHYS; - pginfo.num_pages = num_pages_mr; - pginfo.num_4k = num_pages_4k; - pginfo.num_phys_buf = num_phys_buf; - pginfo.phys_buf_array = phys_buf_array; - pginfo.next_4k = (((u64)iova_start & ~PAGE_MASK) / - EHCA_PAGESIZE); + num_kpages = NUM_CHUNKS(((u64)new_start % PAGE_SIZE) + + new_size, PAGE_SIZE); + num_hwpages = NUM_CHUNKS(((u64)new_start % EHCA_PAGESIZE) + + new_size, EHCA_PAGESIZE); + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_PHYS; + pginfo.num_kpages = num_kpages; + pginfo.num_hwpages = num_hwpages; + pginfo.u.phy.num_phys_buf = num_phys_buf; + pginfo.u.phy.phys_buf_array = phys_buf_array; + pginfo.next_hwpage = (((u64)iova_start & ~PAGE_MASK) / + EHCA_PAGESIZE); } if (mr_rereg_mask & IB_MR_REREG_ACCESS) new_acl = mr_access_flags; @@ -510,7 +519,7 @@ int ehca_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr) struct ehca_pd *my_pd = container_of(mr->pd, struct ehca_pd, ib_pd); u32 cur_pid = current->tgid; unsigned long sl_flags; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_hipzout_parms hipzout; if (my_pd->ib_pd.uobject && my_pd->ib_pd.uobject->context && (my_pd->ownpid != cur_pid)) { @@ -536,14 +545,14 @@ int ehca_query_mr(struct ib_mr *mr, struct ib_mr_attr *mr_attr) "hca_hndl=%lx mr_hndl=%lx lkey=%x", h_ret, mr, shca->ipz_hca_handle.handle, e_mr->ipz_mr_handle.handle, mr->lkey); - ret = ehca_mrmw_map_hrc_query_mr(h_ret); + ret = ehca2ib_return_code(h_ret); goto query_mr_exit1; } - mr_attr->pd = mr->pd; + mr_attr->pd = mr->pd; mr_attr->device_virt_addr = hipzout.vaddr; - mr_attr->size = hipzout.len; - mr_attr->lkey = hipzout.lkey; - mr_attr->rkey = hipzout.rkey; + mr_attr->size = hipzout.len; + mr_attr->lkey = hipzout.lkey; + mr_attr->rkey = hipzout.rkey; ehca_mrmw_reverse_map_acl(&hipzout.acl, &mr_attr->mr_access_flags); query_mr_exit1: @@ -596,7 +605,7 @@ int ehca_dereg_mr(struct ib_mr *mr) "e_mr=%p hca_hndl=%lx mr_hndl=%lx mr->lkey=%x", h_ret, shca, e_mr, shca->ipz_hca_handle.handle, e_mr->ipz_mr_handle.handle, mr->lkey); - ret = ehca_mrmw_map_hrc_free_mr(h_ret); + ret = ehca2ib_return_code(h_ret); goto dereg_mr_exit0; } @@ -622,7 +631,7 @@ struct ib_mw *ehca_alloc_mw(struct ib_pd *pd) struct ehca_pd *e_pd = container_of(pd, struct ehca_pd, ib_pd); struct ehca_shca *shca = container_of(pd->device, struct ehca_shca, ib_device); - struct ehca_mw_hipzout_parms hipzout = {{0},0}; + struct ehca_mw_hipzout_parms hipzout; e_mw = ehca_mw_new(); if (!e_mw) { @@ -636,7 +645,7 @@ struct ib_mw *ehca_alloc_mw(struct ib_pd *pd) ehca_err(pd->device, "hipz_mw_allocate failed, h_ret=%lx " "shca=%p hca_hndl=%lx mw=%p", h_ret, shca, shca->ipz_hca_handle.handle, e_mw); - ib_mw = ERR_PTR(ehca_mrmw_map_hrc_alloc(h_ret)); + ib_mw = ERR_PTR(ehca2ib_return_code(h_ret)); goto alloc_mw_exit1; } /* successful MW allocation */ @@ -679,7 +688,7 @@ int ehca_dealloc_mw(struct ib_mw *mw) "mw=%p rkey=%x hca_hndl=%lx mw_hndl=%lx", h_ret, shca, mw, mw->rkey, shca->ipz_hca_handle.handle, e_mw->ipz_mw_handle.handle); - return ehca_mrmw_map_hrc_free_mw(h_ret); + return ehca2ib_return_code(h_ret); } /* successful deallocation */ ehca_mw_delete(e_mw); @@ -699,7 +708,7 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, struct ehca_mr *e_fmr; int ret; u32 tmp_lkey, tmp_rkey; - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; + struct ehca_mr_pginfo pginfo; /* check other parameters */ if (((mr_access_flags & IB_ACCESS_REMOTE_WRITE) && @@ -745,6 +754,7 @@ struct ib_fmr *ehca_alloc_fmr(struct ib_pd *pd, e_fmr->flags |= EHCA_MR_FLAG_FMR; /* register MR on HCA */ + memset(&pginfo, 0, sizeof(pginfo)); ret = ehca_reg_mr(shca, e_fmr, NULL, fmr_attr->max_pages * (1 << fmr_attr->page_shift), mr_access_flags, e_pd, &pginfo, @@ -783,7 +793,7 @@ int ehca_map_phys_fmr(struct ib_fmr *fmr, container_of(fmr->device, struct ehca_shca, ib_device); struct ehca_mr *e_fmr = container_of(fmr, struct ehca_mr, ib.ib_fmr); struct ehca_pd *e_pd = container_of(fmr->pd, struct ehca_pd, ib_pd); - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; + struct ehca_mr_pginfo pginfo; u32 tmp_lkey, tmp_rkey; if (!(e_fmr->flags & EHCA_MR_FLAG_FMR)) { @@ -809,14 +819,16 @@ int ehca_map_phys_fmr(struct ib_fmr *fmr, fmr, e_fmr->fmr_map_cnt, e_fmr->fmr_max_maps); } - pginfo.type = EHCA_MR_PGI_FMR; - pginfo.num_pages = list_len; - pginfo.num_4k = list_len * (e_fmr->fmr_page_size / EHCA_PAGESIZE); - pginfo.page_list = page_list; - pginfo.next_4k = ((iova & (e_fmr->fmr_page_size-1)) / - EHCA_PAGESIZE); + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_FMR; + pginfo.num_kpages = list_len; + pginfo.num_hwpages = list_len * (e_fmr->fmr_page_size / EHCA_PAGESIZE); + pginfo.u.fmr.page_list = page_list; + pginfo.next_hwpage = ((iova & (e_fmr->fmr_page_size-1)) / + EHCA_PAGESIZE); + pginfo.u.fmr.fmr_pgsize = e_fmr->fmr_page_size; - ret = ehca_rereg_mr(shca, e_fmr, (u64*)iova, + ret = ehca_rereg_mr(shca, e_fmr, (u64 *)iova, list_len * e_fmr->fmr_page_size, e_fmr->acl, e_pd, &pginfo, &tmp_lkey, &tmp_rkey); if (ret) @@ -831,8 +843,7 @@ int ehca_map_phys_fmr(struct ib_fmr *fmr, map_phys_fmr_exit0: if (ret) ehca_err(fmr->device, "ret=%x fmr=%p page_list=%p list_len=%x " - "iova=%lx", - ret, fmr, page_list, list_len, iova); + "iova=%lx", ret, fmr, page_list, list_len, iova); return ret; } /* end ehca_map_phys_fmr() */ @@ -922,7 +933,7 @@ int ehca_dealloc_fmr(struct ib_fmr *fmr) "hca_hndl=%lx fmr_hndl=%lx fmr->lkey=%x", h_ret, e_fmr, shca->ipz_hca_handle.handle, e_fmr->ipz_mr_handle.handle, fmr->lkey); - ret = ehca_mrmw_map_hrc_free_mr(h_ret); + ret = ehca2ib_return_code(h_ret); goto free_fmr_exit0; } /* successful deregistration */ @@ -950,12 +961,12 @@ int ehca_reg_mr(struct ehca_shca *shca, int ret; u64 h_ret; u32 hipz_acl; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); if (ehca_use_hp_mr == 1) - hipz_acl |= 0x00000001; + hipz_acl |= 0x00000001; h_ret = hipz_h_alloc_resource_mr(shca->ipz_hca_handle, e_mr, (u64)iova_start, size, hipz_acl, @@ -963,7 +974,7 @@ int ehca_reg_mr(struct ehca_shca *shca, if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "hipz_alloc_mr failed, h_ret=%lx " "hca_hndl=%lx", h_ret, shca->ipz_hca_handle.handle); - ret = ehca_mrmw_map_hrc_alloc(h_ret); + ret = ehca2ib_return_code(h_ret); goto ehca_reg_mr_exit0; } @@ -974,11 +985,11 @@ int ehca_reg_mr(struct ehca_shca *shca, goto ehca_reg_mr_exit1; /* successful registration */ - e_mr->num_pages = pginfo->num_pages; - e_mr->num_4k = pginfo->num_4k; - e_mr->start = iova_start; - e_mr->size = size; - e_mr->acl = acl; + e_mr->num_kpages = pginfo->num_kpages; + e_mr->num_hwpages = pginfo->num_hwpages; + e_mr->start = iova_start; + e_mr->size = size; + e_mr->acl = acl; *lkey = hipzout.lkey; *rkey = hipzout.rkey; return 0; @@ -988,10 +999,10 @@ ehca_reg_mr_exit1: if (h_ret != H_SUCCESS) { ehca_err(&shca->ib_device, "h_ret=%lx shca=%p e_mr=%p " "iova_start=%p size=%lx acl=%x e_pd=%p lkey=%x " - "pginfo=%p num_pages=%lx num_4k=%lx ret=%x", + "pginfo=%p num_kpages=%lx num_hwpages=%lx ret=%x", h_ret, shca, e_mr, iova_start, size, acl, e_pd, - hipzout.lkey, pginfo, pginfo->num_pages, - pginfo->num_4k, ret); + hipzout.lkey, pginfo, pginfo->num_kpages, + pginfo->num_hwpages, ret); ehca_err(&shca->ib_device, "internal error in ehca_reg_mr, " "not recoverable"); } @@ -999,9 +1010,9 @@ ehca_reg_mr_exit0: if (ret) ehca_err(&shca->ib_device, "ret=%x shca=%p e_mr=%p " "iova_start=%p size=%lx acl=%x e_pd=%p pginfo=%p " - "num_pages=%lx num_4k=%lx", + "num_kpages=%lx num_hwpages=%lx", ret, shca, e_mr, iova_start, size, acl, e_pd, pginfo, - pginfo->num_pages, pginfo->num_4k); + pginfo->num_kpages, pginfo->num_hwpages); return ret; } /* end ehca_reg_mr() */ @@ -1026,24 +1037,24 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, } /* max 512 pages per shot */ - for (i = 0; i < ((pginfo->num_4k + 512 - 1) / 512); i++) { + for (i = 0; i < NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES); i++) { - if (i == ((pginfo->num_4k + 512 - 1) / 512) - 1) { - rnum = pginfo->num_4k % 512; /* last shot */ + if (i == NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES) - 1) { + rnum = pginfo->num_hwpages % MAX_RPAGES; /* last shot */ if (rnum == 0) - rnum = 512; /* last shot is full */ + rnum = MAX_RPAGES; /* last shot is full */ } else - rnum = 512; + rnum = MAX_RPAGES; - if (rnum > 1) { - ret = ehca_set_pagebuf(e_mr, pginfo, rnum, kpage); - if (ret) { - ehca_err(&shca->ib_device, "ehca_set_pagebuf " + ret = ehca_set_pagebuf(pginfo, rnum, kpage); + if (ret) { + ehca_err(&shca->ib_device, "ehca_set_pagebuf " "bad rc, ret=%x rnum=%x kpage=%p", ret, rnum, kpage); - ret = -EFAULT; - goto ehca_reg_mr_rpages_exit1; - } + goto ehca_reg_mr_rpages_exit1; + } + + if (rnum > 1) { rpage = virt_to_abs(kpage); if (!rpage) { ehca_err(&shca->ib_device, "kpage=%p i=%x", @@ -1051,21 +1062,14 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, ret = -EFAULT; goto ehca_reg_mr_rpages_exit1; } - } else { /* rnum==1 */ - ret = ehca_set_pagebuf_1(e_mr, pginfo, &rpage); - if (ret) { - ehca_err(&shca->ib_device, "ehca_set_pagebuf_1 " - "bad rc, ret=%x i=%x", ret, i); - ret = -EFAULT; - goto ehca_reg_mr_rpages_exit1; - } - } + } else + rpage = *kpage; h_ret = hipz_h_register_rpage_mr(shca->ipz_hca_handle, e_mr, 0, /* pagesize 4k */ 0, rpage, rnum); - if (i == ((pginfo->num_4k + 512 - 1) / 512) - 1) { + if (i == NUM_CHUNKS(pginfo->num_hwpages, MAX_RPAGES) - 1) { /* * check for 'registration complete'==H_SUCCESS * and for 'page registered'==H_PAGE_REGISTERED @@ -1078,7 +1082,7 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, shca->ipz_hca_handle.handle, e_mr->ipz_mr_handle.handle, e_mr->ib.ib_mr.lkey); - ret = ehca_mrmw_map_hrc_rrpg_last(h_ret); + ret = ehca2ib_return_code(h_ret); break; } else ret = 0; @@ -1089,7 +1093,7 @@ int ehca_reg_mr_rpages(struct ehca_shca *shca, e_mr->ib.ib_mr.lkey, shca->ipz_hca_handle.handle, e_mr->ipz_mr_handle.handle); - ret = ehca_mrmw_map_hrc_rrpg_notlast(h_ret); + ret = ehca2ib_return_code(h_ret); break; } else ret = 0; @@ -1101,8 +1105,8 @@ ehca_reg_mr_rpages_exit1: ehca_reg_mr_rpages_exit0: if (ret) ehca_err(&shca->ib_device, "ret=%x shca=%p e_mr=%p pginfo=%p " - "num_pages=%lx num_4k=%lx", ret, shca, e_mr, pginfo, - pginfo->num_pages, pginfo->num_4k); + "num_kpages=%lx num_hwpages=%lx", ret, shca, e_mr, + pginfo, pginfo->num_kpages, pginfo->num_hwpages); return ret; } /* end ehca_reg_mr_rpages() */ @@ -1124,7 +1128,7 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, u64 *kpage; u64 rpage; struct ehca_mr_pginfo pginfo_save; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); @@ -1137,12 +1141,12 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, } pginfo_save = *pginfo; - ret = ehca_set_pagebuf(e_mr, pginfo, pginfo->num_4k, kpage); + ret = ehca_set_pagebuf(pginfo, pginfo->num_hwpages, kpage); if (ret) { ehca_err(&shca->ib_device, "set pagebuf failed, e_mr=%p " - "pginfo=%p type=%x num_pages=%lx num_4k=%lx kpage=%p", - e_mr, pginfo, pginfo->type, pginfo->num_pages, - pginfo->num_4k,kpage); + "pginfo=%p type=%x num_kpages=%lx num_hwpages=%lx " + "kpage=%p", e_mr, pginfo, pginfo->type, + pginfo->num_kpages, pginfo->num_hwpages, kpage); goto ehca_rereg_mr_rereg1_exit1; } rpage = virt_to_abs(kpage); @@ -1164,7 +1168,7 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, "(Rereg1), h_ret=%lx e_mr=%p", h_ret, e_mr); *pginfo = pginfo_save; ret = -EAGAIN; - } else if ((u64*)hipzout.vaddr != iova_start) { + } else if ((u64 *)hipzout.vaddr != iova_start) { ehca_err(&shca->ib_device, "PHYP changed iova_start in " "rereg_pmr, iova_start=%p iova_start_out=%lx e_mr=%p " "mr_handle=%lx lkey=%x lkey_out=%x", iova_start, @@ -1176,11 +1180,11 @@ inline int ehca_rereg_mr_rereg1(struct ehca_shca *shca, * successful reregistration * note: start and start_out are identical for eServer HCAs */ - e_mr->num_pages = pginfo->num_pages; - e_mr->num_4k = pginfo->num_4k; - e_mr->start = iova_start; - e_mr->size = size; - e_mr->acl = acl; + e_mr->num_kpages = pginfo->num_kpages; + e_mr->num_hwpages = pginfo->num_hwpages; + e_mr->start = iova_start; + e_mr->size = size; + e_mr->acl = acl; *lkey = hipzout.lkey; *rkey = hipzout.rkey; } @@ -1190,9 +1194,9 @@ ehca_rereg_mr_rereg1_exit1: ehca_rereg_mr_rereg1_exit0: if ( ret && (ret != -EAGAIN) ) ehca_err(&shca->ib_device, "ret=%x lkey=%x rkey=%x " - "pginfo=%p num_pages=%lx num_4k=%lx", - ret, *lkey, *rkey, pginfo, pginfo->num_pages, - pginfo->num_4k); + "pginfo=%p num_kpages=%lx num_hwpages=%lx", + ret, *lkey, *rkey, pginfo, pginfo->num_kpages, + pginfo->num_hwpages); return ret; } /* end ehca_rereg_mr_rereg1() */ @@ -1214,10 +1218,12 @@ int ehca_rereg_mr(struct ehca_shca *shca, int rereg_3_hcall = 0; /* 1: use 3 hipz calls for reregistration */ /* first determine reregistration hCall(s) */ - if ((pginfo->num_4k > 512) || (e_mr->num_4k > 512) || - (pginfo->num_4k > e_mr->num_4k)) { - ehca_dbg(&shca->ib_device, "Rereg3 case, pginfo->num_4k=%lx " - "e_mr->num_4k=%x", pginfo->num_4k, e_mr->num_4k); + if ((pginfo->num_hwpages > MAX_RPAGES) || + (e_mr->num_hwpages > MAX_RPAGES) || + (pginfo->num_hwpages > e_mr->num_hwpages)) { + ehca_dbg(&shca->ib_device, "Rereg3 case, " + "pginfo->num_hwpages=%lx e_mr->num_hwpages=%x", + pginfo->num_hwpages, e_mr->num_hwpages); rereg_1_hcall = 0; rereg_3_hcall = 1; } @@ -1253,7 +1259,7 @@ int ehca_rereg_mr(struct ehca_shca *shca, h_ret, e_mr, shca->ipz_hca_handle.handle, e_mr->ipz_mr_handle.handle, e_mr->ib.ib_mr.lkey); - ret = ehca_mrmw_map_hrc_free_mr(h_ret); + ret = ehca2ib_return_code(h_ret); goto ehca_rereg_mr_exit0; } /* clean ehca_mr_t, without changing struct ib_mr and lock */ @@ -1281,9 +1287,9 @@ ehca_rereg_mr_exit0: if (ret) ehca_err(&shca->ib_device, "ret=%x shca=%p e_mr=%p " "iova_start=%p size=%lx acl=%x e_pd=%p pginfo=%p " - "num_pages=%lx lkey=%x rkey=%x rereg_1_hcall=%x " + "num_kpages=%lx lkey=%x rkey=%x rereg_1_hcall=%x " "rereg_3_hcall=%x", ret, shca, e_mr, iova_start, size, - acl, e_pd, pginfo, pginfo->num_pages, *lkey, *rkey, + acl, e_pd, pginfo, pginfo->num_kpages, *lkey, *rkey, rereg_1_hcall, rereg_3_hcall); return ret; } /* end ehca_rereg_mr() */ @@ -1295,97 +1301,86 @@ int ehca_unmap_one_fmr(struct ehca_shca *shca, { int ret = 0; u64 h_ret; - int rereg_1_hcall = 1; /* 1: use hipz_mr_reregister directly */ - int rereg_3_hcall = 0; /* 1: use 3 hipz calls for unmapping */ struct ehca_pd *e_pd = container_of(e_fmr->ib.ib_fmr.pd, struct ehca_pd, ib_pd); struct ehca_mr save_fmr; u32 tmp_lkey, tmp_rkey; - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_pginfo pginfo; + struct ehca_mr_hipzout_parms hipzout; + struct ehca_mr save_mr; - /* first check if reregistration hCall can be used for unmap */ - if (e_fmr->fmr_max_pages > 512) { - rereg_1_hcall = 0; - rereg_3_hcall = 1; - } - - if (rereg_1_hcall) { + if (e_fmr->fmr_max_pages <= MAX_RPAGES) { /* * note: after using rereg hcall with len=0, * rereg hcall must be used again for registering pages */ h_ret = hipz_h_reregister_pmr(shca->ipz_hca_handle, e_fmr, 0, 0, 0, e_pd->fw_pd, 0, &hipzout); - if (h_ret != H_SUCCESS) { - /* - * should not happen, because length checked above, - * FMRs are not shared and no MW bound to FMRs - */ - ehca_err(&shca->ib_device, "hipz_reregister_pmr failed " - "(Rereg1), h_ret=%lx e_fmr=%p hca_hndl=%lx " - "mr_hndl=%lx lkey=%x lkey_out=%x", - h_ret, e_fmr, shca->ipz_hca_handle.handle, - e_fmr->ipz_mr_handle.handle, - e_fmr->ib.ib_fmr.lkey, hipzout.lkey); - rereg_3_hcall = 1; - } else { + if (h_ret == H_SUCCESS) { /* successful reregistration */ e_fmr->start = NULL; e_fmr->size = 0; tmp_lkey = hipzout.lkey; tmp_rkey = hipzout.rkey; + return 0; } + /* + * should not happen, because length checked above, + * FMRs are not shared and no MW bound to FMRs + */ + ehca_err(&shca->ib_device, "hipz_reregister_pmr failed " + "(Rereg1), h_ret=%lx e_fmr=%p hca_hndl=%lx " + "mr_hndl=%lx lkey=%x lkey_out=%x", + h_ret, e_fmr, shca->ipz_hca_handle.handle, + e_fmr->ipz_mr_handle.handle, + e_fmr->ib.ib_fmr.lkey, hipzout.lkey); + /* try free and rereg */ } - if (rereg_3_hcall) { - struct ehca_mr save_mr; - - /* first free old FMR */ - h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_fmr); - if (h_ret != H_SUCCESS) { - ehca_err(&shca->ib_device, "hipz_free_mr failed, " - "h_ret=%lx e_fmr=%p hca_hndl=%lx mr_hndl=%lx " - "lkey=%x", - h_ret, e_fmr, shca->ipz_hca_handle.handle, - e_fmr->ipz_mr_handle.handle, - e_fmr->ib.ib_fmr.lkey); - ret = ehca_mrmw_map_hrc_free_mr(h_ret); - goto ehca_unmap_one_fmr_exit0; - } - /* clean ehca_mr_t, without changing lock */ - save_fmr = *e_fmr; - ehca_mr_deletenew(e_fmr); - - /* set some MR values */ - e_fmr->flags = save_fmr.flags; - e_fmr->fmr_page_size = save_fmr.fmr_page_size; - e_fmr->fmr_max_pages = save_fmr.fmr_max_pages; - e_fmr->fmr_max_maps = save_fmr.fmr_max_maps; - e_fmr->fmr_map_cnt = save_fmr.fmr_map_cnt; - e_fmr->acl = save_fmr.acl; - - pginfo.type = EHCA_MR_PGI_FMR; - pginfo.num_pages = 0; - pginfo.num_4k = 0; - ret = ehca_reg_mr(shca, e_fmr, NULL, - (e_fmr->fmr_max_pages * e_fmr->fmr_page_size), - e_fmr->acl, e_pd, &pginfo, &tmp_lkey, - &tmp_rkey); - if (ret) { - u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr; - memcpy(&e_fmr->flags, &(save_mr.flags), - sizeof(struct ehca_mr) - offset); - goto ehca_unmap_one_fmr_exit0; - } + /* first free old FMR */ + h_ret = hipz_h_free_resource_mr(shca->ipz_hca_handle, e_fmr); + if (h_ret != H_SUCCESS) { + ehca_err(&shca->ib_device, "hipz_free_mr failed, " + "h_ret=%lx e_fmr=%p hca_hndl=%lx mr_hndl=%lx " + "lkey=%x", + h_ret, e_fmr, shca->ipz_hca_handle.handle, + e_fmr->ipz_mr_handle.handle, + e_fmr->ib.ib_fmr.lkey); + ret = ehca2ib_return_code(h_ret); + goto ehca_unmap_one_fmr_exit0; + } + /* clean ehca_mr_t, without changing lock */ + save_fmr = *e_fmr; + ehca_mr_deletenew(e_fmr); + + /* set some MR values */ + e_fmr->flags = save_fmr.flags; + e_fmr->fmr_page_size = save_fmr.fmr_page_size; + e_fmr->fmr_max_pages = save_fmr.fmr_max_pages; + e_fmr->fmr_max_maps = save_fmr.fmr_max_maps; + e_fmr->fmr_map_cnt = save_fmr.fmr_map_cnt; + e_fmr->acl = save_fmr.acl; + + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_FMR; + pginfo.num_kpages = 0; + pginfo.num_hwpages = 0; + ret = ehca_reg_mr(shca, e_fmr, NULL, + (e_fmr->fmr_max_pages * e_fmr->fmr_page_size), + e_fmr->acl, e_pd, &pginfo, &tmp_lkey, + &tmp_rkey); + if (ret) { + u32 offset = (u64)(&e_fmr->flags) - (u64)e_fmr; + memcpy(&e_fmr->flags, &(save_mr.flags), + sizeof(struct ehca_mr) - offset); + goto ehca_unmap_one_fmr_exit0; } ehca_unmap_one_fmr_exit0: if (ret) ehca_err(&shca->ib_device, "ret=%x tmp_lkey=%x tmp_rkey=%x " - "fmr_max_pages=%x rereg_1_hcall=%x rereg_3_hcall=%x", - ret, tmp_lkey, tmp_rkey, e_fmr->fmr_max_pages, - rereg_1_hcall, rereg_3_hcall); + "fmr_max_pages=%x", + ret, tmp_lkey, tmp_rkey, e_fmr->fmr_max_pages); return ret; } /* end ehca_unmap_one_fmr() */ @@ -1403,7 +1398,7 @@ int ehca_reg_smr(struct ehca_shca *shca, int ret = 0; u64 h_ret; u32 hipz_acl; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); @@ -1419,15 +1414,15 @@ int ehca_reg_smr(struct ehca_shca *shca, shca->ipz_hca_handle.handle, e_origmr->ipz_mr_handle.handle, e_origmr->ib.ib_mr.lkey); - ret = ehca_mrmw_map_hrc_reg_smr(h_ret); + ret = ehca2ib_return_code(h_ret); goto ehca_reg_smr_exit0; } /* successful registration */ - e_newmr->num_pages = e_origmr->num_pages; - e_newmr->num_4k = e_origmr->num_4k; - e_newmr->start = iova_start; - e_newmr->size = e_origmr->size; - e_newmr->acl = acl; + e_newmr->num_kpages = e_origmr->num_kpages; + e_newmr->num_hwpages = e_origmr->num_hwpages; + e_newmr->start = iova_start; + e_newmr->size = e_origmr->size; + e_newmr->acl = acl; e_newmr->ipz_mr_handle = hipzout.handle; *lkey = hipzout.lkey; *rkey = hipzout.rkey; @@ -1453,10 +1448,10 @@ int ehca_reg_internal_maxmr( struct ehca_mr *e_mr; u64 *iova_start; u64 size_maxmr; - struct ehca_mr_pginfo pginfo={0,0,0,0,0,0,0,NULL,0,NULL,NULL,0,NULL,0}; + struct ehca_mr_pginfo pginfo; struct ib_phys_buf ib_pbuf; - u32 num_pages_mr; - u32 num_pages_4k; /* 4k portion "pages" */ + u32 num_kpages; + u32 num_hwpages; e_mr = ehca_mr_new(); if (!e_mr) { @@ -1468,28 +1463,29 @@ int ehca_reg_internal_maxmr( /* register internal max-MR on HCA */ size_maxmr = (u64)high_memory - PAGE_OFFSET; - iova_start = (u64*)KERNELBASE; + iova_start = (u64 *)KERNELBASE; ib_pbuf.addr = 0; ib_pbuf.size = size_maxmr; - num_pages_mr = ((((u64)iova_start % PAGE_SIZE) + size_maxmr + - PAGE_SIZE - 1) / PAGE_SIZE); - num_pages_4k = ((((u64)iova_start % EHCA_PAGESIZE) + size_maxmr + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE); - - pginfo.type = EHCA_MR_PGI_PHYS; - pginfo.num_pages = num_pages_mr; - pginfo.num_4k = num_pages_4k; - pginfo.num_phys_buf = 1; - pginfo.phys_buf_array = &ib_pbuf; + num_kpages = NUM_CHUNKS(((u64)iova_start % PAGE_SIZE) + size_maxmr, + PAGE_SIZE); + num_hwpages = NUM_CHUNKS(((u64)iova_start % EHCA_PAGESIZE) + size_maxmr, + EHCA_PAGESIZE); + + memset(&pginfo, 0, sizeof(pginfo)); + pginfo.type = EHCA_MR_PGI_PHYS; + pginfo.num_kpages = num_kpages; + pginfo.num_hwpages = num_hwpages; + pginfo.u.phy.num_phys_buf = 1; + pginfo.u.phy.phys_buf_array = &ib_pbuf; ret = ehca_reg_mr(shca, e_mr, iova_start, size_maxmr, 0, e_pd, &pginfo, &e_mr->ib.ib_mr.lkey, &e_mr->ib.ib_mr.rkey); if (ret) { ehca_err(&shca->ib_device, "reg of internal max MR failed, " - "e_mr=%p iova_start=%p size_maxmr=%lx num_pages_mr=%x " - "num_pages_4k=%x", e_mr, iova_start, size_maxmr, - num_pages_mr, num_pages_4k); + "e_mr=%p iova_start=%p size_maxmr=%lx num_kpages=%x " + "num_hwpages=%x", e_mr, iova_start, size_maxmr, + num_kpages, num_hwpages); goto ehca_reg_internal_maxmr_exit1; } @@ -1524,7 +1520,7 @@ int ehca_reg_maxmr(struct ehca_shca *shca, u64 h_ret; struct ehca_mr *e_origmr = shca->maxmr; u32 hipz_acl; - struct ehca_mr_hipzout_parms hipzout = {{0},0,0,0,0,0}; + struct ehca_mr_hipzout_parms hipzout; ehca_mrmw_map_acl(acl, &hipz_acl); ehca_mrmw_set_pgsize_hipz_acl(&hipz_acl); @@ -1538,14 +1534,14 @@ int ehca_reg_maxmr(struct ehca_shca *shca, h_ret, e_origmr, shca->ipz_hca_handle.handle, e_origmr->ipz_mr_handle.handle, e_origmr->ib.ib_mr.lkey); - return ehca_mrmw_map_hrc_reg_smr(h_ret); + return ehca2ib_return_code(h_ret); } /* successful registration */ - e_newmr->num_pages = e_origmr->num_pages; - e_newmr->num_4k = e_origmr->num_4k; - e_newmr->start = iova_start; - e_newmr->size = e_origmr->size; - e_newmr->acl = acl; + e_newmr->num_kpages = e_origmr->num_kpages; + e_newmr->num_hwpages = e_origmr->num_hwpages; + e_newmr->start = iova_start; + e_newmr->size = e_origmr->size; + e_newmr->acl = acl; e_newmr->ipz_mr_handle = hipzout.handle; *lkey = hipzout.lkey; *rkey = hipzout.rkey; @@ -1677,299 +1673,187 @@ int ehca_fmr_check_page_list(struct ehca_mr *e_fmr, /*----------------------------------------------------------------------*/ -/* setup page buffer from page info */ -int ehca_set_pagebuf(struct ehca_mr *e_mr, - struct ehca_mr_pginfo *pginfo, - u32 number, - u64 *kpage) +/* PAGE_SIZE >= pginfo->hwpage_size */ +static int ehca_set_pagebuf_user1(struct ehca_mr_pginfo *pginfo, + u32 number, + u64 *kpage) { int ret = 0; struct ib_umem_chunk *prev_chunk; struct ib_umem_chunk *chunk; - struct ib_phys_buf *pbuf; - u64 *fmrlist; - u64 num4k, pgaddr, offs4k; + u64 pgaddr; u32 i = 0; u32 j = 0; - if (pginfo->type == EHCA_MR_PGI_PHYS) { - /* loop over desired phys_buf_array entries */ - while (i < number) { - pbuf = pginfo->phys_buf_array + pginfo->next_buf; - num4k = ((pbuf->addr % EHCA_PAGESIZE) + pbuf->size + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE; - offs4k = (pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE; - while (pginfo->next_4k < offs4k + num4k) { - /* sanity check */ - if ((pginfo->page_cnt >= pginfo->num_pages) || - (pginfo->page_4k_cnt >= pginfo->num_4k)) { - ehca_gen_err("page_cnt >= num_pages, " - "page_cnt=%lx " - "num_pages=%lx " - "page_4k_cnt=%lx " - "num_4k=%lx i=%x", - pginfo->page_cnt, - pginfo->num_pages, - pginfo->page_4k_cnt, - pginfo->num_4k, i); - ret = -EFAULT; - goto ehca_set_pagebuf_exit0; - } - *kpage = phys_to_abs( - (pbuf->addr & EHCA_PAGEMASK) - + (pginfo->next_4k * EHCA_PAGESIZE)); - if ( !(*kpage) && pbuf->addr ) { - ehca_gen_err("pbuf->addr=%lx " - "pbuf->size=%lx " - "next_4k=%lx", pbuf->addr, - pbuf->size, - pginfo->next_4k); - ret = -EFAULT; - goto ehca_set_pagebuf_exit0; - } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; - if (pginfo->next_4k % - (PAGE_SIZE / EHCA_PAGESIZE) == 0) - (pginfo->page_cnt)++; - kpage++; - i++; - if (i >= number) break; - } - if (pginfo->next_4k >= offs4k + num4k) { - (pginfo->next_buf)++; - pginfo->next_4k = 0; - } - } - } else if (pginfo->type == EHCA_MR_PGI_USER) { - /* loop over desired chunk entries */ - chunk = pginfo->next_chunk; - prev_chunk = pginfo->next_chunk; - list_for_each_entry_continue(chunk, - (&(pginfo->region->chunk_list)), - list) { - for (i = pginfo->next_nmap; i < chunk->nmap; ) { - pgaddr = ( page_to_pfn(chunk->page_list[i].page) - << PAGE_SHIFT ); - *kpage = phys_to_abs(pgaddr + - (pginfo->next_4k * - EHCA_PAGESIZE)); - if ( !(*kpage) ) { - ehca_gen_err("pgaddr=%lx " - "chunk->page_list[i]=%lx " - "i=%x next_4k=%lx mr=%p", - pgaddr, - (u64)sg_dma_address( - &chunk-> - page_list[i]), - i, pginfo->next_4k, e_mr); - ret = -EFAULT; - goto ehca_set_pagebuf_exit0; - } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; - kpage++; - if (pginfo->next_4k % - (PAGE_SIZE / EHCA_PAGESIZE) == 0) { - (pginfo->page_cnt)++; - (pginfo->next_nmap)++; - pginfo->next_4k = 0; - i++; - } - j++; - if (j >= number) break; - } - if ((pginfo->next_nmap >= chunk->nmap) && - (j >= number)) { - pginfo->next_nmap = 0; - prev_chunk = chunk; - break; - } else if (pginfo->next_nmap >= chunk->nmap) { - pginfo->next_nmap = 0; - prev_chunk = chunk; - } else if (j >= number) - break; - else - prev_chunk = chunk; - } - pginfo->next_chunk = - list_prepare_entry(prev_chunk, - (&(pginfo->region->chunk_list)), - list); - } else if (pginfo->type == EHCA_MR_PGI_FMR) { - /* loop over desired page_list entries */ - fmrlist = pginfo->page_list + pginfo->next_listelem; - for (i = 0; i < number; i++) { - *kpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) + - pginfo->next_4k * EHCA_PAGESIZE); + /* loop over desired chunk entries */ + chunk = pginfo->u.usr.next_chunk; + prev_chunk = pginfo->u.usr.next_chunk; + list_for_each_entry_continue( + chunk, (&(pginfo->u.usr.region->chunk_list)), list) { + for (i = pginfo->u.usr.next_nmap; i < chunk->nmap; ) { + pgaddr = page_to_pfn(chunk->page_list[i].page) + << PAGE_SHIFT ; + *kpage = phys_to_abs(pgaddr + + (pginfo->next_hwpage * + EHCA_PAGESIZE)); if ( !(*kpage) ) { - ehca_gen_err("*fmrlist=%lx fmrlist=%p " - "next_listelem=%lx next_4k=%lx", - *fmrlist, fmrlist, - pginfo->next_listelem, - pginfo->next_4k); - ret = -EFAULT; - goto ehca_set_pagebuf_exit0; + ehca_gen_err("pgaddr=%lx " + "chunk->page_list[i]=%lx " + "i=%x next_hwpage=%lx", + pgaddr, (u64)sg_dma_address( + &chunk->page_list[i]), + i, pginfo->next_hwpage); + return -EFAULT; } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; + (pginfo->hwpage_cnt)++; + (pginfo->next_hwpage)++; kpage++; - if (pginfo->next_4k % - (e_mr->fmr_page_size / EHCA_PAGESIZE) == 0) { - (pginfo->page_cnt)++; - (pginfo->next_listelem)++; - fmrlist++; - pginfo->next_4k = 0; + if (pginfo->next_hwpage % + (PAGE_SIZE / EHCA_PAGESIZE) == 0) { + (pginfo->kpage_cnt)++; + (pginfo->u.usr.next_nmap)++; + pginfo->next_hwpage = 0; + i++; } + j++; + if (j >= number) break; } - } else { - ehca_gen_err("bad pginfo->type=%x", pginfo->type); - ret = -EFAULT; - goto ehca_set_pagebuf_exit0; + if ((pginfo->u.usr.next_nmap >= chunk->nmap) && + (j >= number)) { + pginfo->u.usr.next_nmap = 0; + prev_chunk = chunk; + break; + } else if (pginfo->u.usr.next_nmap >= chunk->nmap) { + pginfo->u.usr.next_nmap = 0; + prev_chunk = chunk; + } else if (j >= number) + break; + else + prev_chunk = chunk; } - -ehca_set_pagebuf_exit0: - if (ret) - ehca_gen_err("ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx " - "num_4k=%lx next_buf=%lx next_4k=%lx number=%x " - "kpage=%p page_cnt=%lx page_4k_cnt=%lx i=%x " - "next_listelem=%lx region=%p next_chunk=%p " - "next_nmap=%lx", ret, e_mr, pginfo, pginfo->type, - pginfo->num_pages, pginfo->num_4k, - pginfo->next_buf, pginfo->next_4k, number, kpage, - pginfo->page_cnt, pginfo->page_4k_cnt, i, - pginfo->next_listelem, pginfo->region, - pginfo->next_chunk, pginfo->next_nmap); + pginfo->u.usr.next_chunk = + list_prepare_entry(prev_chunk, + (&(pginfo->u.usr.region->chunk_list)), + list); return ret; -} /* end ehca_set_pagebuf() */ - -/*----------------------------------------------------------------------*/ +} -/* setup 1 page from page info page buffer */ -int ehca_set_pagebuf_1(struct ehca_mr *e_mr, - struct ehca_mr_pginfo *pginfo, - u64 *rpage) +int ehca_set_pagebuf_phys(struct ehca_mr_pginfo *pginfo, + u32 number, + u64 *kpage) { int ret = 0; - struct ib_phys_buf *tmp_pbuf; - u64 *fmrlist; - struct ib_umem_chunk *chunk; - struct ib_umem_chunk *prev_chunk; - u64 pgaddr, num4k, offs4k; - - if (pginfo->type == EHCA_MR_PGI_PHYS) { - /* sanity check */ - if ((pginfo->page_cnt >= pginfo->num_pages) || - (pginfo->page_4k_cnt >= pginfo->num_4k)) { - ehca_gen_err("page_cnt >= num_pages, page_cnt=%lx " - "num_pages=%lx page_4k_cnt=%lx num_4k=%lx", - pginfo->page_cnt, pginfo->num_pages, - pginfo->page_4k_cnt, pginfo->num_4k); - ret = -EFAULT; - goto ehca_set_pagebuf_1_exit0; - } - tmp_pbuf = pginfo->phys_buf_array + pginfo->next_buf; - num4k = ((tmp_pbuf->addr % EHCA_PAGESIZE) + tmp_pbuf->size + - EHCA_PAGESIZE - 1) / EHCA_PAGESIZE; - offs4k = (tmp_pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE; - *rpage = phys_to_abs((tmp_pbuf->addr & EHCA_PAGEMASK) + - (pginfo->next_4k * EHCA_PAGESIZE)); - if ( !(*rpage) && tmp_pbuf->addr ) { - ehca_gen_err("tmp_pbuf->addr=%lx" - " tmp_pbuf->size=%lx next_4k=%lx", - tmp_pbuf->addr, tmp_pbuf->size, - pginfo->next_4k); - ret = -EFAULT; - goto ehca_set_pagebuf_1_exit0; - } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; - if (pginfo->next_4k % (PAGE_SIZE / EHCA_PAGESIZE) == 0) - (pginfo->page_cnt)++; - if (pginfo->next_4k >= offs4k + num4k) { - (pginfo->next_buf)++; - pginfo->next_4k = 0; - } - } else if (pginfo->type == EHCA_MR_PGI_USER) { - chunk = pginfo->next_chunk; - prev_chunk = pginfo->next_chunk; - list_for_each_entry_continue(chunk, - (&(pginfo->region->chunk_list)), - list) { - pgaddr = ( page_to_pfn(chunk->page_list[ - pginfo->next_nmap].page) - << PAGE_SHIFT); - *rpage = phys_to_abs(pgaddr + - (pginfo->next_4k * EHCA_PAGESIZE)); - if ( !(*rpage) ) { - ehca_gen_err("pgaddr=%lx chunk->page_list[]=%lx" - " next_nmap=%lx next_4k=%lx mr=%p", - pgaddr, (u64)sg_dma_address( - &chunk->page_list[ - pginfo-> - next_nmap]), - pginfo->next_nmap, pginfo->next_4k, - e_mr); - ret = -EFAULT; - goto ehca_set_pagebuf_1_exit0; - } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; - if (pginfo->next_4k % - (PAGE_SIZE / EHCA_PAGESIZE) == 0) { - (pginfo->page_cnt)++; - (pginfo->next_nmap)++; - pginfo->next_4k = 0; + struct ib_phys_buf *pbuf; + u64 num_hw, offs_hw; + u32 i = 0; + + /* loop over desired phys_buf_array entries */ + while (i < number) { + pbuf = pginfo->u.phy.phys_buf_array + pginfo->u.phy.next_buf; + num_hw = NUM_CHUNKS((pbuf->addr % EHCA_PAGESIZE) + + pbuf->size, EHCA_PAGESIZE); + offs_hw = (pbuf->addr & ~PAGE_MASK) / EHCA_PAGESIZE; + while (pginfo->next_hwpage < offs_hw + num_hw) { + /* sanity check */ + if ((pginfo->kpage_cnt >= pginfo->num_kpages) || + (pginfo->hwpage_cnt >= pginfo->num_hwpages)) { + ehca_gen_err("kpage_cnt >= num_kpages, " + "kpage_cnt=%lx num_kpages=%lx " + "hwpage_cnt=%lx " + "num_hwpages=%lx i=%x", + pginfo->kpage_cnt, + pginfo->num_kpages, + pginfo->hwpage_cnt, + pginfo->num_hwpages, i); + return -EFAULT; } - if (pginfo->next_nmap >= chunk->nmap) { - pginfo->next_nmap = 0; - prev_chunk = chunk; + *kpage = phys_to_abs( + (pbuf->addr & EHCA_PAGEMASK) + + (pginfo->next_hwpage * EHCA_PAGESIZE)); + if ( !(*kpage) && pbuf->addr ) { + ehca_gen_err("pbuf->addr=%lx " + "pbuf->size=%lx " + "next_hwpage=%lx", pbuf->addr, + pbuf->size, + pginfo->next_hwpage); + return -EFAULT; } - break; + (pginfo->hwpage_cnt)++; + (pginfo->next_hwpage)++; + if (pginfo->next_hwpage % + (PAGE_SIZE / EHCA_PAGESIZE) == 0) + (pginfo->kpage_cnt)++; + kpage++; + i++; + if (i >= number) break; + } + if (pginfo->next_hwpage >= offs_hw + num_hw) { + (pginfo->u.phy.next_buf)++; + pginfo->next_hwpage = 0; } - pginfo->next_chunk = - list_prepare_entry(prev_chunk, - (&(pginfo->region->chunk_list)), - list); - } else if (pginfo->type == EHCA_MR_PGI_FMR) { - fmrlist = pginfo->page_list + pginfo->next_listelem; - *rpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) + - pginfo->next_4k * EHCA_PAGESIZE); - if ( !(*rpage) ) { + } + return ret; +} + +int ehca_set_pagebuf_fmr(struct ehca_mr_pginfo *pginfo, + u32 number, + u64 *kpage) +{ + int ret = 0; + u64 *fmrlist; + u32 i; + + /* loop over desired page_list entries */ + fmrlist = pginfo->u.fmr.page_list + pginfo->u.fmr.next_listelem; + for (i = 0; i < number; i++) { + *kpage = phys_to_abs((*fmrlist & EHCA_PAGEMASK) + + pginfo->next_hwpage * EHCA_PAGESIZE); + if ( !(*kpage) ) { ehca_gen_err("*fmrlist=%lx fmrlist=%p " - "next_listelem=%lx next_4k=%lx", - *fmrlist, fmrlist, pginfo->next_listelem, - pginfo->next_4k); - ret = -EFAULT; - goto ehca_set_pagebuf_1_exit0; + "next_listelem=%lx next_hwpage=%lx", + *fmrlist, fmrlist, + pginfo->u.fmr.next_listelem, + pginfo->next_hwpage); + return -EFAULT; } - (pginfo->page_4k_cnt)++; - (pginfo->next_4k)++; - if (pginfo->next_4k % - (e_mr->fmr_page_size / EHCA_PAGESIZE) == 0) { - (pginfo->page_cnt)++; - (pginfo->next_listelem)++; - pginfo->next_4k = 0; + (pginfo->hwpage_cnt)++; + (pginfo->next_hwpage)++; + kpage++; + if (pginfo->next_hwpage % + (pginfo->u.fmr.fmr_pgsize / EHCA_PAGESIZE) == 0) { + (pginfo->kpage_cnt)++; + (pginfo->u.fmr.next_listelem)++; + fmrlist++; + pginfo->next_hwpage = 0; } - } else { + } + return ret; +} + +/* setup page buffer from page info */ +int ehca_set_pagebuf(struct ehca_mr_pginfo *pginfo, + u32 number, + u64 *kpage) +{ + int ret; + + switch (pginfo->type) { + case EHCA_MR_PGI_PHYS: + ret = ehca_set_pagebuf_phys(pginfo, number, kpage); + break; + case EHCA_MR_PGI_USER: + ret = ehca_set_pagebuf_user1(pginfo, number, kpage); + break; + case EHCA_MR_PGI_FMR: + ret = ehca_set_pagebuf_fmr(pginfo, number, kpage); + break; + default: ehca_gen_err("bad pginfo->type=%x", pginfo->type); ret = -EFAULT; - goto ehca_set_pagebuf_1_exit0; + break; } - -ehca_set_pagebuf_1_exit0: - if (ret) - ehca_gen_err("ret=%x e_mr=%p pginfo=%p type=%x num_pages=%lx " - "num_4k=%lx next_buf=%lx next_4k=%lx rpage=%p " - "page_cnt=%lx page_4k_cnt=%lx next_listelem=%lx " - "region=%p next_chunk=%p next_nmap=%lx", ret, e_mr, - pginfo, pginfo->type, pginfo->num_pages, - pginfo->num_4k, pginfo->next_buf, pginfo->next_4k, - rpage, pginfo->page_cnt, pginfo->page_4k_cnt, - pginfo->next_listelem, pginfo->region, - pginfo->next_chunk, pginfo->next_nmap); return ret; -} /* end ehca_set_pagebuf_1() */ +} /* end ehca_set_pagebuf() */ /*----------------------------------------------------------------------*/ @@ -1982,7 +1866,7 @@ int ehca_mr_is_maxmr(u64 size, { /* a MR is treated as max-MR only if it fits following: */ if ((size == ((u64)high_memory - PAGE_OFFSET)) && - (iova_start == (void*)KERNELBASE)) { + (iova_start == (void *)KERNELBASE)) { ehca_gen_dbg("this is a max-MR"); return 1; } else @@ -2042,196 +1926,23 @@ void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl, /*----------------------------------------------------------------------*/ /* - * map HIPZ rc to IB retcodes for MR/MW allocations - * Used for hipz_mr_reg_alloc and hipz_mw_alloc. - */ -int ehca_mrmw_map_hrc_alloc(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* successful completion */ - return 0; - case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */ - case H_CONSTRAINED: /* resource constraint */ - case H_NO_MEM: - return -ENOMEM; - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_alloc() */ - -/*----------------------------------------------------------------------*/ - -/* - * map HIPZ rc to IB retcodes for MR register rpage - * Used for hipz_h_register_rpage_mr at registering last page - */ -int ehca_mrmw_map_hrc_rrpg_last(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* registration complete */ - return 0; - case H_PAGE_REGISTERED: /* page registered */ - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ -/* case H_QT_PARM: invalid queue type */ - case H_PARAMETER: /* - * invalid logical address, - * or count zero or greater 512 - */ - case H_TABLE_FULL: /* page table full */ - case H_HARDWARE: /* HCA not operational */ - return -EINVAL; - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_rrpg_last() */ - -/*----------------------------------------------------------------------*/ - -/* - * map HIPZ rc to IB retcodes for MR register rpage - * Used for hipz_h_register_rpage_mr at registering one page, but not last page - */ -int ehca_mrmw_map_hrc_rrpg_notlast(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_PAGE_REGISTERED: /* page registered */ - return 0; - case H_SUCCESS: /* registration complete */ - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ -/* case H_QT_PARM: invalid queue type */ - case H_PARAMETER: /* - * invalid logical address, - * or count zero or greater 512 - */ - case H_TABLE_FULL: /* page table full */ - case H_HARDWARE: /* HCA not operational */ - return -EINVAL; - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_rrpg_notlast() */ - -/*----------------------------------------------------------------------*/ - -/* map HIPZ rc to IB retcodes for MR query. Used for hipz_mr_query. */ -int ehca_mrmw_map_hrc_query_mr(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* successful completion */ - return 0; - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ - return -EINVAL; - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_query_mr() */ - -/*----------------------------------------------------------------------*/ -/*----------------------------------------------------------------------*/ - -/* - * map HIPZ rc to IB retcodes for freeing MR resource - * Used for hipz_h_free_resource_mr - */ -int ehca_mrmw_map_hrc_free_mr(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* resource freed */ - return 0; - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ - case H_R_STATE: /* invalid resource state */ - case H_HARDWARE: /* HCA not operational */ - return -EINVAL; - case H_RESOURCE: /* Resource in use */ - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_free_mr() */ - -/*----------------------------------------------------------------------*/ - -/* - * map HIPZ rc to IB retcodes for freeing MW resource - * Used for hipz_h_free_resource_mw - */ -int ehca_mrmw_map_hrc_free_mw(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* resource freed */ - return 0; - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ - case H_R_STATE: /* invalid resource state */ - case H_HARDWARE: /* HCA not operational */ - return -EINVAL; - case H_RESOURCE: /* Resource in use */ - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_free_mw() */ - -/*----------------------------------------------------------------------*/ - -/* - * map HIPZ rc to IB retcodes for SMR registrations - * Used for hipz_h_register_smr. - */ -int ehca_mrmw_map_hrc_reg_smr(const u64 hipz_rc) -{ - switch (hipz_rc) { - case H_SUCCESS: /* successful completion */ - return 0; - case H_ADAPTER_PARM: /* invalid adapter handle */ - case H_RH_PARM: /* invalid resource handle */ - case H_MEM_PARM: /* invalid MR virtual address */ - case H_MEM_ACCESS_PARM: /* invalid access controls */ - case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */ - return -EINVAL; - case H_BUSY: /* long busy */ - return -EBUSY; - default: - return -EINVAL; - } -} /* end ehca_mrmw_map_hrc_reg_smr() */ - -/*----------------------------------------------------------------------*/ - -/* * MR destructor and constructor * used in Reregister MR verb, sets all fields in ehca_mr_t to 0, * except struct ib_mr and spinlock */ void ehca_mr_deletenew(struct ehca_mr *mr) { - mr->flags = 0; - mr->num_pages = 0; - mr->num_4k = 0; - mr->acl = 0; - mr->start = NULL; + mr->flags = 0; + mr->num_kpages = 0; + mr->num_hwpages = 0; + mr->acl = 0; + mr->start = NULL; mr->fmr_page_size = 0; mr->fmr_max_pages = 0; - mr->fmr_max_maps = 0; - mr->fmr_map_cnt = 0; + mr->fmr_max_maps = 0; + mr->fmr_map_cnt = 0; memset(&mr->ipz_mr_handle, 0, sizeof(mr->ipz_mr_handle)); memset(&mr->galpas, 0, sizeof(mr->galpas)); - mr->nr_of_pages = 0; - mr->pagearray = NULL; } /* end ehca_mr_deletenew() */ int ehca_init_mrmw_cache(void) diff --git a/drivers/infiniband/hw/ehca/ehca_mrmw.h b/drivers/infiniband/hw/ehca/ehca_mrmw.h index d936e40a574..24f13fe3708 100644 --- a/drivers/infiniband/hw/ehca/ehca_mrmw.h +++ b/drivers/infiniband/hw/ehca/ehca_mrmw.h @@ -101,15 +101,10 @@ int ehca_fmr_check_page_list(struct ehca_mr *e_fmr, u64 *page_list, int list_len); -int ehca_set_pagebuf(struct ehca_mr *e_mr, - struct ehca_mr_pginfo *pginfo, +int ehca_set_pagebuf(struct ehca_mr_pginfo *pginfo, u32 number, u64 *kpage); -int ehca_set_pagebuf_1(struct ehca_mr *e_mr, - struct ehca_mr_pginfo *pginfo, - u64 *rpage); - int ehca_mr_is_maxmr(u64 size, u64 *iova_start); @@ -121,20 +116,6 @@ void ehca_mrmw_set_pgsize_hipz_acl(u32 *hipz_acl); void ehca_mrmw_reverse_map_acl(const u32 *hipz_acl, int *ib_acl); -int ehca_mrmw_map_hrc_alloc(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_rrpg_last(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_rrpg_notlast(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_query_mr(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_free_mr(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_free_mw(const u64 hipz_rc); - -int ehca_mrmw_map_hrc_reg_smr(const u64 hipz_rc); - void ehca_mr_deletenew(struct ehca_mr *mr); #endif /*_EHCA_MRMW_H_*/ diff --git a/drivers/infiniband/hw/ehca/ehca_qes.h b/drivers/infiniband/hw/ehca/ehca_qes.h index 8707d297ce4..818803057eb 100644 --- a/drivers/infiniband/hw/ehca/ehca_qes.h +++ b/drivers/infiniband/hw/ehca/ehca_qes.h @@ -53,13 +53,13 @@ struct ehca_vsgentry { u32 length; }; -#define GRH_FLAG_MASK EHCA_BMASK_IBM(7,7) -#define GRH_IPVERSION_MASK EHCA_BMASK_IBM(0,3) -#define GRH_TCLASS_MASK EHCA_BMASK_IBM(4,12) -#define GRH_FLOWLABEL_MASK EHCA_BMASK_IBM(13,31) -#define GRH_PAYLEN_MASK EHCA_BMASK_IBM(32,47) -#define GRH_NEXTHEADER_MASK EHCA_BMASK_IBM(48,55) -#define GRH_HOPLIMIT_MASK EHCA_BMASK_IBM(56,63) +#define GRH_FLAG_MASK EHCA_BMASK_IBM( 7, 7) +#define GRH_IPVERSION_MASK EHCA_BMASK_IBM( 0, 3) +#define GRH_TCLASS_MASK EHCA_BMASK_IBM( 4, 12) +#define GRH_FLOWLABEL_MASK EHCA_BMASK_IBM(13, 31) +#define GRH_PAYLEN_MASK EHCA_BMASK_IBM(32, 47) +#define GRH_NEXTHEADER_MASK EHCA_BMASK_IBM(48, 55) +#define GRH_HOPLIMIT_MASK EHCA_BMASK_IBM(56, 63) /* * Unreliable Datagram Address Vector Format @@ -206,10 +206,10 @@ struct ehca_wqe { }; -#define WC_SEND_RECEIVE EHCA_BMASK_IBM(0,0) -#define WC_IMM_DATA EHCA_BMASK_IBM(1,1) -#define WC_GRH_PRESENT EHCA_BMASK_IBM(2,2) -#define WC_SE_BIT EHCA_BMASK_IBM(3,3) +#define WC_SEND_RECEIVE EHCA_BMASK_IBM(0, 0) +#define WC_IMM_DATA EHCA_BMASK_IBM(1, 1) +#define WC_GRH_PRESENT EHCA_BMASK_IBM(2, 2) +#define WC_SE_BIT EHCA_BMASK_IBM(3, 3) #define WC_STATUS_ERROR_BIT 0x80000000 #define WC_STATUS_REMOTE_ERROR_FLAGS 0x0000F800 #define WC_STATUS_PURGE_BIT 0x10 diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index 74671250303..48e9ceacd6f 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -602,10 +602,10 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, /* UD circumvention */ parms.act_nr_send_sges -= 2; parms.act_nr_recv_sges -= 2; - swqe_size = offsetof(struct ehca_wqe, - u.ud_av.sg_list[parms.act_nr_send_sges]); - rwqe_size = offsetof(struct ehca_wqe, - u.ud_av.sg_list[parms.act_nr_recv_sges]); + swqe_size = offsetof(struct ehca_wqe, u.ud_av.sg_list[ + parms.act_nr_send_sges]); + rwqe_size = offsetof(struct ehca_wqe, u.ud_av.sg_list[ + parms.act_nr_recv_sges]); } if (IB_QPT_GSI == qp_type || IB_QPT_SMI == qp_type) { @@ -690,8 +690,8 @@ struct ehca_qp *internal_create_qp(struct ib_pd *pd, if (my_qp->send_cq) { ret = ehca_cq_assign_qp(my_qp->send_cq, my_qp); if (ret) { - ehca_err(pd->device, "Couldn't assign qp to send_cq ret=%x", - ret); + ehca_err(pd->device, + "Couldn't assign qp to send_cq ret=%x", ret); goto create_qp_exit4; } } @@ -749,7 +749,7 @@ struct ib_qp *ehca_create_qp(struct ib_pd *pd, struct ehca_qp *ret; ret = internal_create_qp(pd, qp_init_attr, NULL, udata, 0); - return IS_ERR(ret) ? (struct ib_qp *) ret : &ret->ib_qp; + return IS_ERR(ret) ? (struct ib_qp *)ret : &ret->ib_qp; } int internal_destroy_qp(struct ib_device *dev, struct ehca_qp *my_qp, @@ -780,7 +780,7 @@ struct ib_srq *ehca_create_srq(struct ib_pd *pd, my_qp = internal_create_qp(pd, &qp_init_attr, srq_init_attr, udata, 1); if (IS_ERR(my_qp)) - return (struct ib_srq *) my_qp; + return (struct ib_srq *)my_qp; /* copy back return values */ srq_init_attr->attr.max_wr = qp_init_attr.cap.max_recv_wr; @@ -875,7 +875,7 @@ static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca, my_qp, qp_num, h_ret); return ehca2ib_return_code(h_ret); } - bad_send_wqe_p = (void*)((u64)bad_send_wqe_p & (~(1L<<63))); + bad_send_wqe_p = (void *)((u64)bad_send_wqe_p & (~(1L << 63))); ehca_dbg(&shca->ib_device, "qp_num=%x bad_send_wqe_p=%p", qp_num, bad_send_wqe_p); /* convert wqe pointer to vadr */ @@ -890,7 +890,7 @@ static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca, } /* loop sets wqe's purge bit */ - wqe = (struct ehca_wqe*)ipz_qeit_calc(squeue, q_ofs); + wqe = (struct ehca_wqe *)ipz_qeit_calc(squeue, q_ofs); *bad_wqe_cnt = 0; while (wqe->optype != 0xff && wqe->wqef != 0xff) { if (ehca_debug_level) @@ -898,7 +898,7 @@ static int prepare_sqe_rts(struct ehca_qp *my_qp, struct ehca_shca *shca, wqe->nr_of_data_seg = 0; /* suppress data access */ wqe->wqef = WQEF_PURGE; /* WQE to be purged */ q_ofs = ipz_queue_advance_offset(squeue, q_ofs); - wqe = (struct ehca_wqe*)ipz_qeit_calc(squeue, q_ofs); + wqe = (struct ehca_wqe *)ipz_qeit_calc(squeue, q_ofs); *bad_wqe_cnt = (*bad_wqe_cnt)+1; } /* @@ -1003,7 +1003,7 @@ static int internal_modify_qp(struct ib_qp *ibqp, goto modify_qp_exit1; } - ehca_dbg(ibqp->device,"ehca_qp=%p qp_num=%x current qp_state=%x " + ehca_dbg(ibqp->device, "ehca_qp=%p qp_num=%x current qp_state=%x " "new qp_state=%x attribute_mask=%x", my_qp, ibqp->qp_num, qp_cur_state, attr->qp_state, attr_mask); @@ -1019,7 +1019,8 @@ static int internal_modify_qp(struct ib_qp *ibqp, goto modify_qp_exit1; } - if ((mqpcb->qp_state = ib2ehca_qp_state(qp_new_state))) + mqpcb->qp_state = ib2ehca_qp_state(qp_new_state); + if (mqpcb->qp_state) update_mask = EHCA_BMASK_SET(MQPCB_MASK_QP_STATE, 1); else { ret = -EINVAL; @@ -1077,7 +1078,7 @@ static int internal_modify_qp(struct ib_qp *ibqp, spin_lock_irqsave(&my_qp->spinlock_s, flags); squeue_locked = 1; /* mark next free wqe */ - wqe = (struct ehca_wqe*) + wqe = (struct ehca_wqe *) ipz_qeit_get(&my_qp->ipz_squeue); wqe->optype = wqe->wqef = 0xff; ehca_dbg(ibqp->device, "qp_num=%x next_free_wqe=%p", @@ -1312,7 +1313,7 @@ static int internal_modify_qp(struct ib_qp *ibqp, if (h_ret != H_SUCCESS) { ret = ehca2ib_return_code(h_ret); ehca_err(ibqp->device, "hipz_h_modify_qp() failed rc=%lx " - "ehca_qp=%p qp_num=%x",h_ret, my_qp, ibqp->qp_num); + "ehca_qp=%p qp_num=%x", h_ret, my_qp, ibqp->qp_num); goto modify_qp_exit2; } @@ -1411,7 +1412,7 @@ int ehca_query_qp(struct ib_qp *qp, } if (qp_attr_mask & QP_ATTR_QUERY_NOT_SUPPORTED) { - ehca_err(qp->device,"Invalid attribute mask " + ehca_err(qp->device, "Invalid attribute mask " "ehca_qp=%p qp_num=%x qp_attr_mask=%x ", my_qp, qp->qp_num, qp_attr_mask); return -EINVAL; @@ -1419,7 +1420,7 @@ int ehca_query_qp(struct ib_qp *qp, qpcb = ehca_alloc_fw_ctrlblock(GFP_KERNEL); if (!qpcb) { - ehca_err(qp->device,"Out of memory for qpcb " + ehca_err(qp->device, "Out of memory for qpcb " "ehca_qp=%p qp_num=%x", my_qp, qp->qp_num); return -ENOMEM; } @@ -1431,7 +1432,7 @@ int ehca_query_qp(struct ib_qp *qp, if (h_ret != H_SUCCESS) { ret = ehca2ib_return_code(h_ret); - ehca_err(qp->device,"hipz_h_query_qp() failed " + ehca_err(qp->device, "hipz_h_query_qp() failed " "ehca_qp=%p qp_num=%x h_ret=%lx", my_qp, qp->qp_num, h_ret); goto query_qp_exit1; @@ -1442,7 +1443,7 @@ int ehca_query_qp(struct ib_qp *qp, if (qp_attr->cur_qp_state == -EINVAL) { ret = -EINVAL; - ehca_err(qp->device,"Got invalid ehca_qp_state=%x " + ehca_err(qp->device, "Got invalid ehca_qp_state=%x " "ehca_qp=%p qp_num=%x", qpcb->qp_state, my_qp, qp->qp_num); goto query_qp_exit1; diff --git a/drivers/infiniband/hw/ehca/ehca_reqs.c b/drivers/infiniband/hw/ehca/ehca_reqs.c index 61da65e6e5e..94eed70fedf 100644 --- a/drivers/infiniband/hw/ehca/ehca_reqs.c +++ b/drivers/infiniband/hw/ehca/ehca_reqs.c @@ -79,7 +79,8 @@ static inline int ehca_write_rwqe(struct ipz_queue *ipz_rqueue, } if (ehca_debug_level) { - ehca_gen_dbg("RECEIVE WQE written into ipz_rqueue=%p", ipz_rqueue); + ehca_gen_dbg("RECEIVE WQE written into ipz_rqueue=%p", + ipz_rqueue); ehca_dmp( wqe_p, 16*(6 + wqe_p->nr_of_data_seg), "recv wqe"); } @@ -99,7 +100,7 @@ static void trace_send_wr_ud(const struct ib_send_wr *send_wr) struct ib_mad_hdr *mad_hdr = send_wr->wr.ud.mad_hdr; struct ib_sge *sge = send_wr->sg_list; ehca_gen_dbg("send_wr#%x wr_id=%lx num_sge=%x " - "send_flags=%x opcode=%x",idx, send_wr->wr_id, + "send_flags=%x opcode=%x", idx, send_wr->wr_id, send_wr->num_sge, send_wr->send_flags, send_wr->opcode); if (mad_hdr) { @@ -116,7 +117,7 @@ static void trace_send_wr_ud(const struct ib_send_wr *send_wr) mad_hdr->attr_mod); } for (j = 0; j < send_wr->num_sge; j++) { - u8 *data = (u8 *) abs_to_virt(sge->addr); + u8 *data = (u8 *)abs_to_virt(sge->addr); ehca_gen_dbg("send_wr#%x sge#%x addr=%p length=%x " "lkey=%x", idx, j, data, sge->length, sge->lkey); @@ -534,9 +535,11 @@ poll_cq_one_read_cqe: cqe_count++; if (unlikely(cqe->status & WC_STATUS_PURGE_BIT)) { - struct ehca_qp *qp=ehca_cq_get_qp(my_cq, cqe->local_qp_number); + struct ehca_qp *qp; int purgeflag; unsigned long flags; + + qp = ehca_cq_get_qp(my_cq, cqe->local_qp_number); if (!qp) { ehca_err(cq->device, "cq_num=%x qp_num=%x " "could not find qp -> ignore cqe", @@ -551,8 +554,8 @@ poll_cq_one_read_cqe: spin_unlock_irqrestore(&qp->spinlock_s, flags); if (purgeflag) { - ehca_dbg(cq->device, "Got CQE with purged bit qp_num=%x " - "src_qp=%x", + ehca_dbg(cq->device, + "Got CQE with purged bit qp_num=%x src_qp=%x", cqe->local_qp_number, cqe->remote_qp_number); if (ehca_debug_level) ehca_dmp(cqe, 64, "qp_num=%x src_qp=%x", diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h index 03b185f873d..678b8139186 100644 --- a/drivers/infiniband/hw/ehca/ehca_tools.h +++ b/drivers/infiniband/hw/ehca/ehca_tools.h @@ -93,14 +93,14 @@ extern int ehca_debug_level; #define ehca_gen_dbg(format, arg...) \ do { \ if (unlikely(ehca_debug_level)) \ - printk(KERN_DEBUG "PU%04x EHCA_DBG:%s " format "\n",\ + printk(KERN_DEBUG "PU%04x EHCA_DBG:%s " format "\n", \ get_paca()->paca_index, __FUNCTION__, ## arg); \ } while (0) #define ehca_gen_warn(format, arg...) \ do { \ if (unlikely(ehca_debug_level)) \ - printk(KERN_INFO "PU%04x EHCA_WARN:%s " format "\n",\ + printk(KERN_INFO "PU%04x EHCA_WARN:%s " format "\n", \ get_paca()->paca_index, __FUNCTION__, ## arg); \ } while (0) @@ -114,12 +114,12 @@ extern int ehca_debug_level; * <format string> adr=X ofs=Y <8 bytes hex> <8 bytes hex> */ #define ehca_dmp(adr, len, format, args...) \ - do { \ - unsigned int x; \ + do { \ + unsigned int x; \ unsigned int l = (unsigned int)(len); \ - unsigned char *deb = (unsigned char*)(adr); \ + unsigned char *deb = (unsigned char *)(adr); \ for (x = 0; x < l; x += 16) { \ - printk("EHCA_DMP:%s " format \ + printk(KERN_INFO "EHCA_DMP:%s " format \ " adr=%p ofs=%04x %016lx %016lx\n", \ __FUNCTION__, ##args, deb, x, \ *((u64 *)&deb[0]), *((u64 *)&deb[8])); \ @@ -128,16 +128,16 @@ extern int ehca_debug_level; } while (0) /* define a bitmask, little endian version */ -#define EHCA_BMASK(pos,length) (((pos)<<16)+(length)) +#define EHCA_BMASK(pos, length) (((pos) << 16) + (length)) /* define a bitmask, the ibm way... */ -#define EHCA_BMASK_IBM(from,to) (((63-to)<<16)+((to)-(from)+1)) +#define EHCA_BMASK_IBM(from, to) (((63 - to) << 16) + ((to) - (from) + 1)) /* internal function, don't use */ -#define EHCA_BMASK_SHIFTPOS(mask) (((mask)>>16)&0xffff) +#define EHCA_BMASK_SHIFTPOS(mask) (((mask) >> 16) & 0xffff) /* internal function, don't use */ -#define EHCA_BMASK_MASK(mask) (0xffffffffffffffffULL >> ((64-(mask))&0xffff)) +#define EHCA_BMASK_MASK(mask) (~0ULL >> ((64 - (mask)) & 0xffff)) /** * EHCA_BMASK_SET - return value shifted and masked by mask @@ -145,14 +145,14 @@ extern int ehca_debug_level; * variable&=~EHCA_BMASK_SET(MY_MASK,-1) clears the bits from the mask * in variable */ -#define EHCA_BMASK_SET(mask,value) \ - ((EHCA_BMASK_MASK(mask) & ((u64)(value)))<<EHCA_BMASK_SHIFTPOS(mask)) +#define EHCA_BMASK_SET(mask, value) \ + ((EHCA_BMASK_MASK(mask) & ((u64)(value))) << EHCA_BMASK_SHIFTPOS(mask)) /** * EHCA_BMASK_GET - extract a parameter from value by mask */ -#define EHCA_BMASK_GET(mask,value) \ - (EHCA_BMASK_MASK(mask)& (((u64)(value))>>EHCA_BMASK_SHIFTPOS(mask))) +#define EHCA_BMASK_GET(mask, value) \ + (EHCA_BMASK_MASK(mask) & (((u64)(value)) >> EHCA_BMASK_SHIFTPOS(mask))) /* Converts ehca to ib return code */ @@ -161,8 +161,11 @@ static inline int ehca2ib_return_code(u64 ehca_rc) switch (ehca_rc) { case H_SUCCESS: return 0; + case H_RESOURCE: /* Resource in use */ case H_BUSY: return -EBUSY; + case H_NOT_ENOUGH_RESOURCES: /* insufficient resources */ + case H_CONSTRAINED: /* resource constraint */ case H_NO_MEM: return -ENOMEM; default: diff --git a/drivers/infiniband/hw/ehca/ehca_uverbs.c b/drivers/infiniband/hw/ehca/ehca_uverbs.c index 3031b3bb56f..05c415744e3 100644 --- a/drivers/infiniband/hw/ehca/ehca_uverbs.c +++ b/drivers/infiniband/hw/ehca/ehca_uverbs.c @@ -70,7 +70,7 @@ int ehca_dealloc_ucontext(struct ib_ucontext *context) static void ehca_mm_open(struct vm_area_struct *vma) { - u32 *count = (u32*)vma->vm_private_data; + u32 *count = (u32 *)vma->vm_private_data; if (!count) { ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx", vma->vm_start, vma->vm_end); @@ -86,7 +86,7 @@ static void ehca_mm_open(struct vm_area_struct *vma) static void ehca_mm_close(struct vm_area_struct *vma) { - u32 *count = (u32*)vma->vm_private_data; + u32 *count = (u32 *)vma->vm_private_data; if (!count) { ehca_gen_err("Invalid vma struct vm_start=%lx vm_end=%lx", vma->vm_start, vma->vm_end); @@ -215,7 +215,8 @@ static int ehca_mmap_qp(struct vm_area_struct *vma, struct ehca_qp *qp, case 2: /* qp rqueue_addr */ ehca_dbg(qp->ib_qp.device, "qp_num=%x rqueue", qp->ib_qp.qp_num); - ret = ehca_mmap_queue(vma, &qp->ipz_rqueue, &qp->mm_count_rqueue); + ret = ehca_mmap_queue(vma, &qp->ipz_rqueue, + &qp->mm_count_rqueue); if (unlikely(ret)) { ehca_err(qp->ib_qp.device, "ehca_mmap_queue(rq) failed rc=%x qp_num=%x", @@ -227,7 +228,8 @@ static int ehca_mmap_qp(struct vm_area_struct *vma, struct ehca_qp *qp, case 3: /* qp squeue_addr */ ehca_dbg(qp->ib_qp.device, "qp_num=%x squeue", qp->ib_qp.qp_num); - ret = ehca_mmap_queue(vma, &qp->ipz_squeue, &qp->mm_count_squeue); + ret = ehca_mmap_queue(vma, &qp->ipz_squeue, + &qp->mm_count_squeue); if (unlikely(ret)) { ehca_err(qp->ib_qp.device, "ehca_mmap_queue(sq) failed rc=%x qp_num=%x", diff --git a/drivers/infiniband/hw/ehca/hcp_if.c b/drivers/infiniband/hw/ehca/hcp_if.c index 4776a8b0fee..3394e05f4b4 100644 --- a/drivers/infiniband/hw/ehca/hcp_if.c +++ b/drivers/infiniband/hw/ehca/hcp_if.c @@ -501,8 +501,8 @@ u64 hipz_h_register_rpage_qp(const struct ipz_adapter_handle adapter_handle, return H_PARAMETER; } - return hipz_h_register_rpage(adapter_handle,pagesize,queue_type, - qp_handle.handle,logical_address_of_page, + return hipz_h_register_rpage(adapter_handle, pagesize, queue_type, + qp_handle.handle, logical_address_of_page, count); } @@ -522,9 +522,9 @@ u64 hipz_h_disable_and_get_wqe(const struct ipz_adapter_handle adapter_handle, qp_handle.handle, /* r6 */ 0, 0, 0, 0, 0, 0); if (log_addr_next_sq_wqe2processed) - *log_addr_next_sq_wqe2processed = (void*)outs[0]; + *log_addr_next_sq_wqe2processed = (void *)outs[0]; if (log_addr_next_rq_wqe2processed) - *log_addr_next_rq_wqe2processed = (void*)outs[1]; + *log_addr_next_rq_wqe2processed = (void *)outs[1]; return ret; } diff --git a/drivers/infiniband/hw/ehca/hcp_phyp.c b/drivers/infiniband/hw/ehca/hcp_phyp.c index 0b1a4772c78..214821095cb 100644 --- a/drivers/infiniband/hw/ehca/hcp_phyp.c +++ b/drivers/infiniband/hw/ehca/hcp_phyp.c @@ -50,7 +50,7 @@ int hcall_map_page(u64 physaddr, u64 *mapaddr) int hcall_unmap_page(u64 mapaddr) { - iounmap((volatile void __iomem*)mapaddr); + iounmap((volatile void __iomem *) mapaddr); return 0; } diff --git a/drivers/infiniband/hw/ehca/hipz_fns_core.h b/drivers/infiniband/hw/ehca/hipz_fns_core.h index 20898a15344..868735fd318 100644 --- a/drivers/infiniband/hw/ehca/hipz_fns_core.h +++ b/drivers/infiniband/hw/ehca/hipz_fns_core.h @@ -53,10 +53,10 @@ #define hipz_galpa_load_cq(gal, offset) \ hipz_galpa_load(gal, CQTEMM_OFFSET(offset)) -#define hipz_galpa_store_qp(gal,offset, value) \ +#define hipz_galpa_store_qp(gal, offset, value) \ hipz_galpa_store(gal, QPTEMM_OFFSET(offset), value) #define hipz_galpa_load_qp(gal, offset) \ - hipz_galpa_load(gal,QPTEMM_OFFSET(offset)) + hipz_galpa_load(gal, QPTEMM_OFFSET(offset)) static inline void hipz_update_sqa(struct ehca_qp *qp, u16 nr_wqes) { diff --git a/drivers/infiniband/hw/ehca/hipz_hw.h b/drivers/infiniband/hw/ehca/hipz_hw.h index dad6dea5636..d9739e55451 100644 --- a/drivers/infiniband/hw/ehca/hipz_hw.h +++ b/drivers/infiniband/hw/ehca/hipz_hw.h @@ -161,11 +161,11 @@ struct hipz_qptemm { /* 0x1000 */ }; -#define QPX_SQADDER EHCA_BMASK_IBM(48,63) -#define QPX_RQADDER EHCA_BMASK_IBM(48,63) -#define QPX_AAELOG_RESET_SRQ_LIMIT EHCA_BMASK_IBM(3,3) +#define QPX_SQADDER EHCA_BMASK_IBM(48, 63) +#define QPX_RQADDER EHCA_BMASK_IBM(48, 63) +#define QPX_AAELOG_RESET_SRQ_LIMIT EHCA_BMASK_IBM(3, 3) -#define QPTEMM_OFFSET(x) offsetof(struct hipz_qptemm,x) +#define QPTEMM_OFFSET(x) offsetof(struct hipz_qptemm, x) /* MRMWPT Entry Memory Map */ struct hipz_mrmwmm { @@ -187,7 +187,7 @@ struct hipz_mrmwmm { }; -#define MRMWMM_OFFSET(x) offsetof(struct hipz_mrmwmm,x) +#define MRMWMM_OFFSET(x) offsetof(struct hipz_mrmwmm, x) struct hipz_qpedmm { /* 0x00 */ @@ -238,7 +238,7 @@ struct hipz_qpedmm { u64 qpedx_rrva3; }; -#define QPEDMM_OFFSET(x) offsetof(struct hipz_qpedmm,x) +#define QPEDMM_OFFSET(x) offsetof(struct hipz_qpedmm, x) /* CQ Table Entry Memory Map */ struct hipz_cqtemm { @@ -263,12 +263,12 @@ struct hipz_cqtemm { /* 0x1000 */ }; -#define CQX_FEC_CQE_CNT EHCA_BMASK_IBM(32,63) -#define CQX_FECADDER EHCA_BMASK_IBM(32,63) -#define CQX_N0_GENERATE_SOLICITED_COMP_EVENT EHCA_BMASK_IBM(0,0) -#define CQX_N1_GENERATE_COMP_EVENT EHCA_BMASK_IBM(0,0) +#define CQX_FEC_CQE_CNT EHCA_BMASK_IBM(32, 63) +#define CQX_FECADDER EHCA_BMASK_IBM(32, 63) +#define CQX_N0_GENERATE_SOLICITED_COMP_EVENT EHCA_BMASK_IBM(0, 0) +#define CQX_N1_GENERATE_COMP_EVENT EHCA_BMASK_IBM(0, 0) -#define CQTEMM_OFFSET(x) offsetof(struct hipz_cqtemm,x) +#define CQTEMM_OFFSET(x) offsetof(struct hipz_cqtemm, x) /* EQ Table Entry Memory Map */ struct hipz_eqtemm { @@ -293,7 +293,7 @@ struct hipz_eqtemm { }; -#define EQTEMM_OFFSET(x) offsetof(struct hipz_eqtemm,x) +#define EQTEMM_OFFSET(x) offsetof(struct hipz_eqtemm, x) /* access control defines for MR/MW */ #define HIPZ_ACCESSCTRL_L_WRITE 0x00800000 diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.c b/drivers/infiniband/hw/ehca/ipz_pt_fn.c index bf7a40088f6..9606f13ed09 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.c +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.c @@ -114,7 +114,7 @@ int ipz_queue_ctor(struct ipz_queue *queue, */ f = 0; while (f < nr_of_pages) { - u8 *kpage = (u8*)get_zeroed_page(GFP_KERNEL); + u8 *kpage = (u8 *)get_zeroed_page(GFP_KERNEL); int k; if (!kpage) goto ipz_queue_ctor_exit0; /*NOMEM*/ diff --git a/drivers/infiniband/hw/ehca/ipz_pt_fn.h b/drivers/infiniband/hw/ehca/ipz_pt_fn.h index 007f0882fd4..39a4f64aff4 100644 --- a/drivers/infiniband/hw/ehca/ipz_pt_fn.h +++ b/drivers/infiniband/hw/ehca/ipz_pt_fn.h @@ -240,7 +240,7 @@ void *ipz_qeit_eq_get_inc(struct ipz_queue *queue); static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue) { void *ret = ipz_qeit_get(queue); - u32 qe = *(u8 *) ret; + u32 qe = *(u8 *)ret; if ((qe >> 7) != (queue->toggle_state & 1)) return NULL; ipz_qeit_eq_get_inc(queue); /* this is a good one */ @@ -250,7 +250,7 @@ static inline void *ipz_eqit_eq_get_inc_valid(struct ipz_queue *queue) static inline void *ipz_eqit_eq_peek_valid(struct ipz_queue *queue) { void *ret = ipz_qeit_get(queue); - u32 qe = *(u8 *) ret; + u32 qe = *(u8 *)ret; if ((qe >> 7) != (queue->toggle_state & 1)) return NULL; return ret; diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c index 9361f5ab8bd..09c5fd84b1e 100644 --- a/drivers/infiniband/hw/ipath/ipath_driver.c +++ b/drivers/infiniband/hw/ipath/ipath_driver.c @@ -1889,7 +1889,7 @@ void ipath_write_kreg_port(const struct ipath_devdata *dd, ipath_kreg regno, /* Below is "non-zero" to force override, but both actual LEDs are off */ #define LED_OVER_BOTH_OFF (8) -void ipath_run_led_override(unsigned long opaque) +static void ipath_run_led_override(unsigned long opaque) { struct ipath_devdata *dd = (struct ipath_devdata *)opaque; int timeoff; diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c index 6b9147964a4..b4503e9c1e9 100644 --- a/drivers/infiniband/hw/ipath/ipath_eeprom.c +++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c @@ -426,8 +426,8 @@ bail: * @buffer: data to write * @len: number of bytes to write */ -int ipath_eeprom_internal_write(struct ipath_devdata *dd, u8 eeprom_offset, - const void *buffer, int len) +static int ipath_eeprom_internal_write(struct ipath_devdata *dd, u8 eeprom_offset, + const void *buffer, int len) { u8 single_byte; int sub_len; diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c index 47aa43428fb..1fd91c59f24 100644 --- a/drivers/infiniband/hw/ipath/ipath_intr.c +++ b/drivers/infiniband/hw/ipath/ipath_intr.c @@ -70,7 +70,7 @@ static void ipath_clrpiobuf(struct ipath_devdata *dd, u32 pnum) * If rewrite is true, and bits are set in the sendbufferror registers, * we'll write to the buffer, for error recovery on parity errors. */ -void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite) +static void ipath_disarm_senderrbufs(struct ipath_devdata *dd, int rewrite) { u32 piobcnt; unsigned long sbuf[4]; diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h index 3105005fc9d..ace63ef78e6 100644 --- a/drivers/infiniband/hw/ipath/ipath_kernel.h +++ b/drivers/infiniband/hw/ipath/ipath_kernel.h @@ -776,7 +776,6 @@ void ipath_get_eeprom_info(struct ipath_devdata *); int ipath_update_eeprom_log(struct ipath_devdata *dd); void ipath_inc_eeprom_err(struct ipath_devdata *dd, u32 eidx, u32 incr); u64 ipath_snap_cntr(struct ipath_devdata *, ipath_creg); -void ipath_disarm_senderrbufs(struct ipath_devdata *, int); /* * Set LED override, only the two LSBs have "public" meaning, but @@ -820,7 +819,6 @@ static inline u64 ipath_mdio_req(int cmd, int dev, int reg, int data) #define IPATH_MDIO_CTRL_8355_REG_10 0x1D int ipath_get_user_pages(unsigned long, size_t, struct page **); -int ipath_get_user_pages_nocopy(unsigned long, struct page **); void ipath_release_user_pages(struct page **, size_t); void ipath_release_user_pages_on_close(struct page **, size_t); int ipath_eeprom_read(struct ipath_devdata *, u8, void *, int); diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index 85256747d8a..c69c2523944 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -507,7 +507,7 @@ static int want_buffer(struct ipath_devdata *dd) * * Called when we run out of PIO buffers. */ -void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev) +static void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev) { unsigned long flags; diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c index 27034d38b3d..0190edc8044 100644 --- a/drivers/infiniband/hw/ipath/ipath_user_pages.c +++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c @@ -171,32 +171,6 @@ int ipath_get_user_pages(unsigned long start_page, size_t num_pages, return ret; } -/** - * ipath_get_user_pages_nocopy - lock a single page for I/O and mark shared - * @start_page: the page to lock - * @p: the output page structure - * - * This is similar to ipath_get_user_pages, but it's always one page, and we - * mark the page as locked for I/O, and shared. This is used for the user - * process page that contains the destination address for the rcvhdrq tail - * update, so we need to have the vma. If we don't do this, the page can be - * taken away from us on fork, even if the child never touches it, and then - * the user process never sees the tail register updates. - */ -int ipath_get_user_pages_nocopy(unsigned long page, struct page **p) -{ - struct vm_area_struct *vma; - int ret; - - down_write(¤t->mm->mmap_sem); - - ret = __get_user_pages(page, 1, p, &vma); - - up_write(¤t->mm->mmap_sem); - - return ret; -} - void ipath_release_user_pages(struct page **p, size_t num_pages) { down_write(¤t->mm->mmap_sem); diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c index 65f7181e9cf..16aa61fd808 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.c +++ b/drivers/infiniband/hw/ipath/ipath_verbs.c @@ -488,7 +488,7 @@ bail:; * This is called from ipath_do_rcv_timer() at interrupt level to check for * QPs which need retransmits and to collect performance numbers. */ -void ipath_ib_timer(struct ipath_ibdev *dev) +static void ipath_ib_timer(struct ipath_ibdev *dev) { struct ipath_qp *resend = NULL; struct list_head *last; diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h index f3d1f2cee6f..9bbe81967f1 100644 --- a/drivers/infiniband/hw/ipath/ipath_verbs.h +++ b/drivers/infiniband/hw/ipath/ipath_verbs.h @@ -782,8 +782,6 @@ void ipath_update_mmap_info(struct ipath_ibdev *dev, int ipath_mmap(struct ib_ucontext *context, struct vm_area_struct *vma); -void ipath_no_bufs_available(struct ipath_qp *qp, struct ipath_ibdev *dev); - void ipath_insert_rnr_queue(struct ipath_qp *qp); int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only); @@ -807,8 +805,6 @@ void ipath_ib_rcv(struct ipath_ibdev *, void *, void *, u32); int ipath_ib_piobufavail(struct ipath_ibdev *); -void ipath_ib_timer(struct ipath_ibdev *); - unsigned ipath_get_npkeys(struct ipath_devdata *); u32 ipath_get_cr_errpkey(struct ipath_devdata *); diff --git a/drivers/infiniband/hw/mlx4/qp.c b/drivers/infiniband/hw/mlx4/qp.c index 40042184ad5..b5a24fbef70 100644 --- a/drivers/infiniband/hw/mlx4/qp.c +++ b/drivers/infiniband/hw/mlx4/qp.c @@ -1183,6 +1183,43 @@ static int mlx4_wq_overflow(struct mlx4_ib_wq *wq, int nreq, struct ib_cq *ib_cq return cur + nreq >= wq->max_post; } +static __always_inline void set_raddr_seg(struct mlx4_wqe_raddr_seg *rseg, + u64 remote_addr, u32 rkey) +{ + rseg->raddr = cpu_to_be64(remote_addr); + rseg->rkey = cpu_to_be32(rkey); + rseg->reserved = 0; +} + +static void set_atomic_seg(struct mlx4_wqe_atomic_seg *aseg, struct ib_send_wr *wr) +{ + if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap); + aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add); + } else { + aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add); + aseg->compare = 0; + } + +} + +static void set_datagram_seg(struct mlx4_wqe_datagram_seg *dseg, + struct ib_send_wr *wr) +{ + memcpy(dseg->av, &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av)); + dseg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn); + dseg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey); + +} + +static void set_data_seg(struct mlx4_wqe_data_seg *dseg, + struct ib_sge *sg) +{ + dseg->byte_count = cpu_to_be32(sg->length); + dseg->lkey = cpu_to_be32(sg->lkey); + dseg->addr = cpu_to_be64(sg->addr); +} + int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, struct ib_send_wr **bad_wr) { @@ -1238,26 +1275,13 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - ((struct mlx4_wqe_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.atomic.remote_addr); - ((struct mlx4_wqe_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.atomic.rkey); - ((struct mlx4_wqe_raddr_seg *) wqe)->reserved = 0; - + set_raddr_seg(wqe, wr->wr.atomic.remote_addr, + wr->wr.atomic.rkey); wqe += sizeof (struct mlx4_wqe_raddr_seg); - if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { - ((struct mlx4_wqe_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.swap); - ((struct mlx4_wqe_atomic_seg *) wqe)->compare = - cpu_to_be64(wr->wr.atomic.compare_add); - } else { - ((struct mlx4_wqe_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.compare_add); - ((struct mlx4_wqe_atomic_seg *) wqe)->compare = 0; - } - + set_atomic_seg(wqe, wr); wqe += sizeof (struct mlx4_wqe_atomic_seg); + size += (sizeof (struct mlx4_wqe_raddr_seg) + sizeof (struct mlx4_wqe_atomic_seg)) / 16; @@ -1266,15 +1290,10 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_WR_RDMA_READ: case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: - ((struct mlx4_wqe_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.rdma.remote_addr); - ((struct mlx4_wqe_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.rdma.rkey); - ((struct mlx4_wqe_raddr_seg *) wqe)->reserved = 0; - + set_raddr_seg(wqe, wr->wr.rdma.remote_addr, + wr->wr.rdma.rkey); wqe += sizeof (struct mlx4_wqe_raddr_seg); size += sizeof (struct mlx4_wqe_raddr_seg) / 16; - break; default: @@ -1284,13 +1303,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; case IB_QPT_UD: - memcpy(((struct mlx4_wqe_datagram_seg *) wqe)->av, - &to_mah(wr->wr.ud.ah)->av, sizeof (struct mlx4_av)); - ((struct mlx4_wqe_datagram_seg *) wqe)->dqpn = - cpu_to_be32(wr->wr.ud.remote_qpn); - ((struct mlx4_wqe_datagram_seg *) wqe)->qkey = - cpu_to_be32(wr->wr.ud.remote_qkey); - + set_datagram_seg(wqe, wr); wqe += sizeof (struct mlx4_wqe_datagram_seg); size += sizeof (struct mlx4_wqe_datagram_seg) / 16; break; @@ -1313,12 +1326,7 @@ int mlx4_ib_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mlx4_wqe_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mlx4_wqe_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mlx4_wqe_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); + set_data_seg(wqe, wr->sg_list + i); wqe += sizeof (struct mlx4_wqe_data_seg); size += sizeof (struct mlx4_wqe_data_seg) / 16; @@ -1498,7 +1506,7 @@ static int to_ib_qp_access_flags(int mlx4_flags) static void to_ib_ah_attr(struct mlx4_dev *dev, struct ib_ah_attr *ib_ah_attr, struct mlx4_qp_path *path) { - memset(ib_ah_attr, 0, sizeof *path); + memset(ib_ah_attr, 0, sizeof *ib_ah_attr); ib_ah_attr->port_num = path->sched_queue & 0x40 ? 2 : 1; if (ib_ah_attr->port_num == 0 || ib_ah_attr->port_num > dev->caps.num_ports) @@ -1515,7 +1523,7 @@ static void to_ib_ah_attr(struct mlx4_dev *dev, struct ib_ah_attr *ib_ah_attr, ib_ah_attr->grh.traffic_class = (be32_to_cpu(path->tclass_flowlabel) >> 20) & 0xff; ib_ah_attr->grh.flow_label = - be32_to_cpu(path->tclass_flowlabel) & 0xffffff; + be32_to_cpu(path->tclass_flowlabel) & 0xfffff; memcpy(ib_ah_attr->grh.dgid.raw, path->rgid, sizeof ib_ah_attr->grh.dgid.raw); } @@ -1560,7 +1568,10 @@ int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr } qp_attr->pkey_index = context.pri_path.pkey_index & 0x7f; - qp_attr->port_num = context.pri_path.sched_queue & 0x40 ? 2 : 1; + if (qp_attr->qp_state == IB_QPS_INIT) + qp_attr->port_num = qp->port; + else + qp_attr->port_num = context.pri_path.sched_queue & 0x40 ? 2 : 1; /* qp_attr->en_sqd_async_notify is only applicable in modify qp */ qp_attr->sq_draining = mlx4_state == MLX4_QP_STATE_SQ_DRAINING; @@ -1578,17 +1589,25 @@ int mlx4_ib_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, int qp_attr done: qp_attr->cur_qp_state = qp_attr->qp_state; + qp_attr->cap.max_recv_wr = qp->rq.wqe_cnt; + qp_attr->cap.max_recv_sge = qp->rq.max_gs; + if (!ibqp->uobject) { - qp_attr->cap.max_send_wr = qp->sq.wqe_cnt; - qp_attr->cap.max_recv_wr = qp->rq.wqe_cnt; - qp_attr->cap.max_send_sge = qp->sq.max_gs; - qp_attr->cap.max_recv_sge = qp->rq.max_gs; - qp_attr->cap.max_inline_data = (1 << qp->sq.wqe_shift) - - send_wqe_overhead(qp->ibqp.qp_type) - - sizeof (struct mlx4_wqe_inline_seg); - qp_init_attr->cap = qp_attr->cap; + qp_attr->cap.max_send_wr = qp->sq.wqe_cnt; + qp_attr->cap.max_send_sge = qp->sq.max_gs; + } else { + qp_attr->cap.max_send_wr = 0; + qp_attr->cap.max_send_sge = 0; } + /* + * We don't support inline sends for kernel QPs (yet), and we + * don't know what userspace's value should be. + */ + qp_attr->cap.max_inline_data = 0; + + qp_init_attr->cap = qp_attr->cap; + return 0; } diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c index aa563e61de6..76fed7545c5 100644 --- a/drivers/infiniband/hw/mthca/mthca_main.c +++ b/drivers/infiniband/hw/mthca/mthca_main.c @@ -67,7 +67,7 @@ MODULE_PARM_DESC(msi_x, "attempt to use MSI-X if nonzero"); static int msi = 0; module_param(msi, int, 0444); -MODULE_PARM_DESC(msi, "attempt to use MSI if nonzero"); +MODULE_PARM_DESC(msi, "attempt to use MSI if nonzero (deprecated, use MSI-X instead)"); #else /* CONFIG_PCI_MSI */ @@ -1117,9 +1117,21 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) if (msi_x && !mthca_enable_msi_x(mdev)) mdev->mthca_flags |= MTHCA_FLAG_MSI_X; - if (msi && !(mdev->mthca_flags & MTHCA_FLAG_MSI_X) && - !pci_enable_msi(pdev)) - mdev->mthca_flags |= MTHCA_FLAG_MSI; + else if (msi) { + static int warned; + + if (!warned) { + printk(KERN_WARNING PFX "WARNING: MSI support will be " + "removed from the ib_mthca driver in January 2008.\n"); + printk(KERN_WARNING " If you are using MSI and cannot " + "switch to MSI-X, please tell " + "<general@lists.openfabrics.org>.\n"); + ++warned; + } + + if (!pci_enable_msi(pdev)) + mdev->mthca_flags |= MTHCA_FLAG_MSI; + } if (mthca_cmd_init(mdev)) { mthca_err(mdev, "Failed to init command interface, aborting.\n"); @@ -1135,7 +1147,7 @@ static int __mthca_init_one(struct pci_dev *pdev, int hca_type) goto err_cmd; if (mdev->fw_ver < mthca_hca_table[hca_type].latest_fw) { - mthca_warn(mdev, "HCA FW version %d.%d.%3d is old (%d.%d.%3d is current).\n", + mthca_warn(mdev, "HCA FW version %d.%d.%03d is old (%d.%d.%03d is current).\n", (int) (mdev->fw_ver >> 32), (int) (mdev->fw_ver >> 16) & 0xffff, (int) (mdev->fw_ver & 0xffff), (int) (mthca_hca_table[hca_type].latest_fw >> 32), diff --git a/drivers/infiniband/hw/mthca/mthca_qp.c b/drivers/infiniband/hw/mthca/mthca_qp.c index 11f1d99db40..df01b2026a6 100644 --- a/drivers/infiniband/hw/mthca/mthca_qp.c +++ b/drivers/infiniband/hw/mthca/mthca_qp.c @@ -1578,6 +1578,45 @@ static inline int mthca_wq_overflow(struct mthca_wq *wq, int nreq, return cur + nreq >= wq->max; } +static __always_inline void set_raddr_seg(struct mthca_raddr_seg *rseg, + u64 remote_addr, u32 rkey) +{ + rseg->raddr = cpu_to_be64(remote_addr); + rseg->rkey = cpu_to_be32(rkey); + rseg->reserved = 0; +} + +static __always_inline void set_atomic_seg(struct mthca_atomic_seg *aseg, + struct ib_send_wr *wr) +{ + if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { + aseg->swap_add = cpu_to_be64(wr->wr.atomic.swap); + aseg->compare = cpu_to_be64(wr->wr.atomic.compare_add); + } else { + aseg->swap_add = cpu_to_be64(wr->wr.atomic.compare_add); + aseg->compare = 0; + } + +} + +static void set_tavor_ud_seg(struct mthca_tavor_ud_seg *useg, + struct ib_send_wr *wr) +{ + useg->lkey = cpu_to_be32(to_mah(wr->wr.ud.ah)->key); + useg->av_addr = cpu_to_be64(to_mah(wr->wr.ud.ah)->avdma); + useg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn); + useg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey); + +} + +static void set_arbel_ud_seg(struct mthca_arbel_ud_seg *useg, + struct ib_send_wr *wr) +{ + memcpy(useg->av, to_mah(wr->wr.ud.ah)->av, MTHCA_AV_SIZE); + useg->dqpn = cpu_to_be32(wr->wr.ud.remote_qpn); + useg->qkey = cpu_to_be32(wr->wr.ud.remote_qkey); +} + int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, struct ib_send_wr **bad_wr) { @@ -1590,8 +1629,15 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, int nreq; int i; int size; - int size0 = 0; - u32 f0 = 0; + /* + * f0 and size0 are only used if nreq != 0, and they will + * always be initialized the first time through the main loop + * before nreq is incremented. So nreq cannot become non-zero + * without initializing f0 and size0, and they are in fact + * never used uninitialized. + */ + int uninitialized_var(size0); + u32 uninitialized_var(f0); int ind; u8 op0 = 0; @@ -1636,25 +1682,11 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.atomic.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.atomic.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - + set_raddr_seg(wqe, wr->wr.atomic.remote_addr, + wr->wr.atomic.rkey); wqe += sizeof (struct mthca_raddr_seg); - if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { - ((struct mthca_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.swap); - ((struct mthca_atomic_seg *) wqe)->compare = - cpu_to_be64(wr->wr.atomic.compare_add); - } else { - ((struct mthca_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.compare_add); - ((struct mthca_atomic_seg *) wqe)->compare = 0; - } - + set_atomic_seg(wqe, wr); wqe += sizeof (struct mthca_atomic_seg); size += (sizeof (struct mthca_raddr_seg) + sizeof (struct mthca_atomic_seg)) / 16; @@ -1663,12 +1695,9 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: case IB_WR_RDMA_READ: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.rdma.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.rdma.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - wqe += sizeof (struct mthca_raddr_seg); + set_raddr_seg(wqe, wr->wr.rdma.remote_addr, + wr->wr.rdma.rkey); + wqe += sizeof (struct mthca_raddr_seg); size += sizeof (struct mthca_raddr_seg) / 16; break; @@ -1683,12 +1712,9 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.rdma.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.rdma.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - wqe += sizeof (struct mthca_raddr_seg); + set_raddr_seg(wqe, wr->wr.rdma.remote_addr, + wr->wr.rdma.rkey); + wqe += sizeof (struct mthca_raddr_seg); size += sizeof (struct mthca_raddr_seg) / 16; break; @@ -1700,16 +1726,8 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; case UD: - ((struct mthca_tavor_ud_seg *) wqe)->lkey = - cpu_to_be32(to_mah(wr->wr.ud.ah)->key); - ((struct mthca_tavor_ud_seg *) wqe)->av_addr = - cpu_to_be64(to_mah(wr->wr.ud.ah)->avdma); - ((struct mthca_tavor_ud_seg *) wqe)->dqpn = - cpu_to_be32(wr->wr.ud.remote_qpn); - ((struct mthca_tavor_ud_seg *) wqe)->qkey = - cpu_to_be32(wr->wr.ud.remote_qkey); - - wqe += sizeof (struct mthca_tavor_ud_seg); + set_tavor_ud_seg(wqe, wr); + wqe += sizeof (struct mthca_tavor_ud_seg); size += sizeof (struct mthca_tavor_ud_seg) / 16; break; @@ -1734,13 +1752,8 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); - wqe += sizeof (struct mthca_data_seg); + mthca_set_data_seg(wqe, wr->sg_list + i); + wqe += sizeof (struct mthca_data_seg); size += sizeof (struct mthca_data_seg) / 16; } @@ -1768,11 +1781,11 @@ int mthca_tavor_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, mthca_opcode[wr->opcode]); wmb(); ((struct mthca_next_seg *) prev_wqe)->ee_nds = - cpu_to_be32((size0 ? 0 : MTHCA_NEXT_DBD) | size | + cpu_to_be32((nreq ? 0 : MTHCA_NEXT_DBD) | size | ((wr->send_flags & IB_SEND_FENCE) ? MTHCA_NEXT_FENCE : 0)); - if (!size0) { + if (!nreq) { size0 = size; op0 = mthca_opcode[wr->opcode]; f0 = wr->send_flags & IB_SEND_FENCE ? @@ -1822,7 +1835,14 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, int nreq; int i; int size; - int size0 = 0; + /* + * size0 is only used if nreq != 0, and it will always be + * initialized the first time through the main loop before + * nreq is incremented. So nreq cannot become non-zero + * without initializing size0, and it is in fact never used + * uninitialized. + */ + int uninitialized_var(size0); int ind; void *wqe; void *prev_wqe; @@ -1863,13 +1883,8 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); - wqe += sizeof (struct mthca_data_seg); + mthca_set_data_seg(wqe, wr->sg_list + i); + wqe += sizeof (struct mthca_data_seg); size += sizeof (struct mthca_data_seg) / 16; } @@ -1881,7 +1896,7 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, ((struct mthca_next_seg *) prev_wqe)->ee_nds = cpu_to_be32(MTHCA_NEXT_DBD | size); - if (!size0) + if (!nreq) size0 = size; ++ind; @@ -1903,7 +1918,6 @@ int mthca_tavor_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, qp->rq.next_ind = ind; qp->rq.head += MTHCA_TAVOR_MAX_WQES_PER_RECV_DB; - size0 = 0; } } @@ -1945,8 +1959,15 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, int nreq; int i; int size; - int size0 = 0; - u32 f0 = 0; + /* + * f0 and size0 are only used if nreq != 0, and they will + * always be initialized the first time through the main loop + * before nreq is incremented. So nreq cannot become non-zero + * without initializing f0 and size0, and they are in fact + * never used uninitialized. + */ + int uninitialized_var(size0); + u32 uninitialized_var(f0); int ind; u8 op0 = 0; @@ -1966,7 +1987,6 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, doorbell[1] = cpu_to_be32((qp->qpn << 8) | size0); qp->sq.head += MTHCA_ARBEL_MAX_WQES_PER_SEND_DB; - size0 = 0; /* * Make sure that descriptors are written before @@ -2017,26 +2037,12 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_ATOMIC_CMP_AND_SWP: case IB_WR_ATOMIC_FETCH_AND_ADD: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.atomic.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.atomic.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - + set_raddr_seg(wqe, wr->wr.atomic.remote_addr, + wr->wr.atomic.rkey); wqe += sizeof (struct mthca_raddr_seg); - if (wr->opcode == IB_WR_ATOMIC_CMP_AND_SWP) { - ((struct mthca_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.swap); - ((struct mthca_atomic_seg *) wqe)->compare = - cpu_to_be64(wr->wr.atomic.compare_add); - } else { - ((struct mthca_atomic_seg *) wqe)->swap_add = - cpu_to_be64(wr->wr.atomic.compare_add); - ((struct mthca_atomic_seg *) wqe)->compare = 0; - } - - wqe += sizeof (struct mthca_atomic_seg); + set_atomic_seg(wqe, wr); + wqe += sizeof (struct mthca_atomic_seg); size += (sizeof (struct mthca_raddr_seg) + sizeof (struct mthca_atomic_seg)) / 16; break; @@ -2044,12 +2050,9 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, case IB_WR_RDMA_READ: case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.rdma.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.rdma.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - wqe += sizeof (struct mthca_raddr_seg); + set_raddr_seg(wqe, wr->wr.rdma.remote_addr, + wr->wr.rdma.rkey); + wqe += sizeof (struct mthca_raddr_seg); size += sizeof (struct mthca_raddr_seg) / 16; break; @@ -2064,12 +2067,9 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, switch (wr->opcode) { case IB_WR_RDMA_WRITE: case IB_WR_RDMA_WRITE_WITH_IMM: - ((struct mthca_raddr_seg *) wqe)->raddr = - cpu_to_be64(wr->wr.rdma.remote_addr); - ((struct mthca_raddr_seg *) wqe)->rkey = - cpu_to_be32(wr->wr.rdma.rkey); - ((struct mthca_raddr_seg *) wqe)->reserved = 0; - wqe += sizeof (struct mthca_raddr_seg); + set_raddr_seg(wqe, wr->wr.rdma.remote_addr, + wr->wr.rdma.rkey); + wqe += sizeof (struct mthca_raddr_seg); size += sizeof (struct mthca_raddr_seg) / 16; break; @@ -2081,14 +2081,8 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, break; case UD: - memcpy(((struct mthca_arbel_ud_seg *) wqe)->av, - to_mah(wr->wr.ud.ah)->av, MTHCA_AV_SIZE); - ((struct mthca_arbel_ud_seg *) wqe)->dqpn = - cpu_to_be32(wr->wr.ud.remote_qpn); - ((struct mthca_arbel_ud_seg *) wqe)->qkey = - cpu_to_be32(wr->wr.ud.remote_qkey); - - wqe += sizeof (struct mthca_arbel_ud_seg); + set_arbel_ud_seg(wqe, wr); + wqe += sizeof (struct mthca_arbel_ud_seg); size += sizeof (struct mthca_arbel_ud_seg) / 16; break; @@ -2113,13 +2107,8 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); - wqe += sizeof (struct mthca_data_seg); + mthca_set_data_seg(wqe, wr->sg_list + i); + wqe += sizeof (struct mthca_data_seg); size += sizeof (struct mthca_data_seg) / 16; } @@ -2151,7 +2140,7 @@ int mthca_arbel_post_send(struct ib_qp *ibqp, struct ib_send_wr *wr, ((wr->send_flags & IB_SEND_FENCE) ? MTHCA_NEXT_FENCE : 0)); - if (!size0) { + if (!nreq) { size0 = size; op0 = mthca_opcode[wr->opcode]; f0 = wr->send_flags & IB_SEND_FENCE ? @@ -2241,20 +2230,12 @@ int mthca_arbel_post_receive(struct ib_qp *ibqp, struct ib_recv_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); + mthca_set_data_seg(wqe, wr->sg_list + i); wqe += sizeof (struct mthca_data_seg); } - if (i < qp->rq.max_gs) { - ((struct mthca_data_seg *) wqe)->byte_count = 0; - ((struct mthca_data_seg *) wqe)->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); - ((struct mthca_data_seg *) wqe)->addr = 0; - } + if (i < qp->rq.max_gs) + mthca_set_data_seg_inval(wqe); qp->wrid[ind] = wr->wr_id; diff --git a/drivers/infiniband/hw/mthca/mthca_srq.c b/drivers/infiniband/hw/mthca/mthca_srq.c index b8f05a52667..88d219e730a 100644 --- a/drivers/infiniband/hw/mthca/mthca_srq.c +++ b/drivers/infiniband/hw/mthca/mthca_srq.c @@ -543,20 +543,12 @@ int mthca_tavor_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); + mthca_set_data_seg(wqe, wr->sg_list + i); wqe += sizeof (struct mthca_data_seg); } - if (i < srq->max_gs) { - ((struct mthca_data_seg *) wqe)->byte_count = 0; - ((struct mthca_data_seg *) wqe)->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); - ((struct mthca_data_seg *) wqe)->addr = 0; - } + if (i < srq->max_gs) + mthca_set_data_seg_inval(wqe); ((struct mthca_next_seg *) prev_wqe)->nda_op = cpu_to_be32((ind << srq->wqe_shift) | 1); @@ -662,20 +654,12 @@ int mthca_arbel_post_srq_recv(struct ib_srq *ibsrq, struct ib_recv_wr *wr, } for (i = 0; i < wr->num_sge; ++i) { - ((struct mthca_data_seg *) wqe)->byte_count = - cpu_to_be32(wr->sg_list[i].length); - ((struct mthca_data_seg *) wqe)->lkey = - cpu_to_be32(wr->sg_list[i].lkey); - ((struct mthca_data_seg *) wqe)->addr = - cpu_to_be64(wr->sg_list[i].addr); + mthca_set_data_seg(wqe, wr->sg_list + i); wqe += sizeof (struct mthca_data_seg); } - if (i < srq->max_gs) { - ((struct mthca_data_seg *) wqe)->byte_count = 0; - ((struct mthca_data_seg *) wqe)->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); - ((struct mthca_data_seg *) wqe)->addr = 0; - } + if (i < srq->max_gs) + mthca_set_data_seg_inval(wqe); srq->wrid[ind] = wr->wr_id; srq->first_free = next_ind; diff --git a/drivers/infiniband/hw/mthca/mthca_wqe.h b/drivers/infiniband/hw/mthca/mthca_wqe.h index e7d2c1e8619..f6a66fe78e4 100644 --- a/drivers/infiniband/hw/mthca/mthca_wqe.h +++ b/drivers/infiniband/hw/mthca/mthca_wqe.h @@ -113,4 +113,19 @@ struct mthca_mlx_seg { __be16 vcrc; }; +static __always_inline void mthca_set_data_seg(struct mthca_data_seg *dseg, + struct ib_sge *sg) +{ + dseg->byte_count = cpu_to_be32(sg->length); + dseg->lkey = cpu_to_be32(sg->lkey); + dseg->addr = cpu_to_be64(sg->addr); +} + +static __always_inline void mthca_set_data_seg_inval(struct mthca_data_seg *dseg) +{ + dseg->byte_count = 0; + dseg->lkey = cpu_to_be32(MTHCA_INVAL_LKEY); + dseg->addr = 0; +} + #endif /* MTHCA_WQE_H */ diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h index e2353701e8b..1ee867b1b34 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.h +++ b/drivers/infiniband/ulp/iser/iscsi_iser.h @@ -310,8 +310,6 @@ int iser_conn_init(struct iser_conn **ib_conn); void iser_conn_terminate(struct iser_conn *ib_conn); -void iser_conn_release(struct iser_conn *ib_conn); - void iser_rcv_completion(struct iser_desc *desc, unsigned long dto_xfer_len); @@ -329,9 +327,6 @@ void iser_reg_single(struct iser_device *device, struct iser_regd_buf *regd_buf, enum dma_data_direction direction); -int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask, - enum iser_data_dir cmd_dir); - void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask, enum iser_data_dir cmd_dir); diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c index fc9f1fd0ae5..36cdf77ae92 100644 --- a/drivers/infiniband/ulp/iser/iser_memory.c +++ b/drivers/infiniband/ulp/iser/iser_memory.c @@ -103,8 +103,8 @@ void iser_reg_single(struct iser_device *device, /** * iser_start_rdma_unaligned_sg */ -int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask, - enum iser_data_dir cmd_dir) +static int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask, + enum iser_data_dir cmd_dir) { int dma_nents; struct ib_device *dev; diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index 2044de1164a..d42ec0156ee 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -311,6 +311,29 @@ static int iser_conn_state_comp_exch(struct iser_conn *ib_conn, } /** + * Frees all conn objects and deallocs conn descriptor + */ +static void iser_conn_release(struct iser_conn *ib_conn) +{ + struct iser_device *device = ib_conn->device; + + BUG_ON(ib_conn->state != ISER_CONN_DOWN); + + mutex_lock(&ig.connlist_mutex); + list_del(&ib_conn->conn_list); + mutex_unlock(&ig.connlist_mutex); + + iser_free_ib_conn_res(ib_conn); + ib_conn->device = NULL; + /* on EVENT_ADDR_ERROR there's no device yet for this conn */ + if (device != NULL) + iser_device_try_release(device); + if (ib_conn->iser_conn) + ib_conn->iser_conn->ib_conn = NULL; + kfree(ib_conn); +} + +/** * triggers start of the disconnect procedures and wait for them to be done */ void iser_conn_terminate(struct iser_conn *ib_conn) @@ -550,30 +573,6 @@ connect_failure: } /** - * Frees all conn objects and deallocs conn descriptor - */ -void iser_conn_release(struct iser_conn *ib_conn) -{ - struct iser_device *device = ib_conn->device; - - BUG_ON(ib_conn->state != ISER_CONN_DOWN); - - mutex_lock(&ig.connlist_mutex); - list_del(&ib_conn->conn_list); - mutex_unlock(&ig.connlist_mutex); - - iser_free_ib_conn_res(ib_conn); - ib_conn->device = NULL; - /* on EVENT_ADDR_ERROR there's no device yet for this conn */ - if (device != NULL) - iser_device_try_release(device); - if (ib_conn->iser_conn) - ib_conn->iser_conn->ib_conn = NULL; - kfree(ib_conn); -} - - -/** * iser_reg_page_vec - Register physical memory * * returns: 0 on success, errno code on failure diff --git a/drivers/net/mlx4/catas.c b/drivers/net/mlx4/catas.c index 1bb088aeaf7..6b32ec94b3a 100644 --- a/drivers/net/mlx4/catas.c +++ b/drivers/net/mlx4/catas.c @@ -30,41 +30,133 @@ * SOFTWARE. */ +#include <linux/workqueue.h> + #include "mlx4.h" -void mlx4_handle_catas_err(struct mlx4_dev *dev) +enum { + MLX4_CATAS_POLL_INTERVAL = 5 * HZ, +}; + +static DEFINE_SPINLOCK(catas_lock); + +static LIST_HEAD(catas_list); +static struct workqueue_struct *catas_wq; +static struct work_struct catas_work; + +static int internal_err_reset = 1; +module_param(internal_err_reset, int, 0644); +MODULE_PARM_DESC(internal_err_reset, + "Reset device on internal errors if non-zero (default 1)"); + +static void dump_err_buf(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); int i; - mlx4_err(dev, "Catastrophic error detected:\n"); + mlx4_err(dev, "Internal error detected:\n"); for (i = 0; i < priv->fw.catas_size; ++i) mlx4_err(dev, " buf[%02x]: %08x\n", i, swab32(readl(priv->catas_err.map + i))); +} - mlx4_dispatch_event(dev, MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR, 0, 0); +static void poll_catas(unsigned long dev_ptr) +{ + struct mlx4_dev *dev = (struct mlx4_dev *) dev_ptr; + struct mlx4_priv *priv = mlx4_priv(dev); + + if (readl(priv->catas_err.map)) { + dump_err_buf(dev); + + mlx4_dispatch_event(dev, MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR, 0, 0); + + if (internal_err_reset) { + spin_lock(&catas_lock); + list_add(&priv->catas_err.list, &catas_list); + spin_unlock(&catas_lock); + + queue_work(catas_wq, &catas_work); + } + } else + mod_timer(&priv->catas_err.timer, + round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL)); } -void mlx4_map_catas_buf(struct mlx4_dev *dev) +static void catas_reset(struct work_struct *work) +{ + struct mlx4_priv *priv, *tmppriv; + struct mlx4_dev *dev; + + LIST_HEAD(tlist); + int ret; + + spin_lock_irq(&catas_lock); + list_splice_init(&catas_list, &tlist); + spin_unlock_irq(&catas_lock); + + list_for_each_entry_safe(priv, tmppriv, &tlist, catas_err.list) { + ret = mlx4_restart_one(priv->dev.pdev); + dev = &priv->dev; + if (ret) + mlx4_err(dev, "Reset failed (%d)\n", ret); + else + mlx4_dbg(dev, "Reset succeeded\n"); + } +} + +void mlx4_start_catas_poll(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); unsigned long addr; + INIT_LIST_HEAD(&priv->catas_err.list); + init_timer(&priv->catas_err.timer); + priv->catas_err.map = NULL; + addr = pci_resource_start(dev->pdev, priv->fw.catas_bar) + priv->fw.catas_offset; priv->catas_err.map = ioremap(addr, priv->fw.catas_size * 4); - if (!priv->catas_err.map) - mlx4_warn(dev, "Failed to map catastrophic error buffer at 0x%lx\n", + if (!priv->catas_err.map) { + mlx4_warn(dev, "Failed to map internal error buffer at 0x%lx\n", addr); + return; + } + priv->catas_err.timer.data = (unsigned long) dev; + priv->catas_err.timer.function = poll_catas; + priv->catas_err.timer.expires = + round_jiffies(jiffies + MLX4_CATAS_POLL_INTERVAL); + add_timer(&priv->catas_err.timer); } -void mlx4_unmap_catas_buf(struct mlx4_dev *dev) +void mlx4_stop_catas_poll(struct mlx4_dev *dev) { struct mlx4_priv *priv = mlx4_priv(dev); + del_timer_sync(&priv->catas_err.timer); + if (priv->catas_err.map) iounmap(priv->catas_err.map); + + spin_lock_irq(&catas_lock); + list_del(&priv->catas_err.list); + spin_unlock_irq(&catas_lock); +} + +int __init mlx4_catas_init(void) +{ + INIT_WORK(&catas_work, catas_reset); + + catas_wq = create_singlethread_workqueue("mlx4_err"); + if (!catas_wq) + return -ENOMEM; + + return 0; +} + +void mlx4_catas_cleanup(void) +{ + destroy_workqueue(catas_wq); } diff --git a/drivers/net/mlx4/eq.c b/drivers/net/mlx4/eq.c index 27a82cecd69..2095c843fa1 100644 --- a/drivers/net/mlx4/eq.c +++ b/drivers/net/mlx4/eq.c @@ -89,14 +89,12 @@ struct mlx4_eq_context { (1ull << MLX4_EVENT_TYPE_PATH_MIG_FAILED) | \ (1ull << MLX4_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \ (1ull << MLX4_EVENT_TYPE_WQ_ACCESS_ERROR) | \ - (1ull << MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR) | \ (1ull << MLX4_EVENT_TYPE_PORT_CHANGE) | \ (1ull << MLX4_EVENT_TYPE_ECC_DETECT) | \ (1ull << MLX4_EVENT_TYPE_SRQ_CATAS_ERROR) | \ (1ull << MLX4_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ (1ull << MLX4_EVENT_TYPE_SRQ_LIMIT) | \ (1ull << MLX4_EVENT_TYPE_CMD)) -#define MLX4_CATAS_EVENT_MASK (1ull << MLX4_EVENT_TYPE_LOCAL_CATAS_ERROR) struct mlx4_eqe { u8 reserved1; @@ -264,7 +262,7 @@ static irqreturn_t mlx4_interrupt(int irq, void *dev_ptr) writel(priv->eq_table.clr_mask, priv->eq_table.clr_int); - for (i = 0; i < MLX4_EQ_CATAS; ++i) + for (i = 0; i < MLX4_NUM_EQ; ++i) work |= mlx4_eq_int(dev, &priv->eq_table.eq[i]); return IRQ_RETVAL(work); @@ -281,14 +279,6 @@ static irqreturn_t mlx4_msi_x_interrupt(int irq, void *eq_ptr) return IRQ_HANDLED; } -static irqreturn_t mlx4_catas_interrupt(int irq, void *dev_ptr) -{ - mlx4_handle_catas_err(dev_ptr); - - /* MSI-X vectors always belong to us */ - return IRQ_HANDLED; -} - static int mlx4_MAP_EQ(struct mlx4_dev *dev, u64 event_mask, int unmap, int eq_num) { @@ -490,11 +480,9 @@ static void mlx4_free_irqs(struct mlx4_dev *dev) if (eq_table->have_irq) free_irq(dev->pdev->irq, dev); - for (i = 0; i < MLX4_EQ_CATAS; ++i) + for (i = 0; i < MLX4_NUM_EQ; ++i) if (eq_table->eq[i].have_irq) free_irq(eq_table->eq[i].irq, eq_table->eq + i); - if (eq_table->eq[MLX4_EQ_CATAS].have_irq) - free_irq(eq_table->eq[MLX4_EQ_CATAS].irq, dev); } static int __devinit mlx4_map_clr_int(struct mlx4_dev *dev) @@ -598,32 +586,19 @@ int __devinit mlx4_init_eq_table(struct mlx4_dev *dev) if (dev->flags & MLX4_FLAG_MSI_X) { static const char *eq_name[] = { [MLX4_EQ_COMP] = DRV_NAME " (comp)", - [MLX4_EQ_ASYNC] = DRV_NAME " (async)", - [MLX4_EQ_CATAS] = DRV_NAME " (catas)" + [MLX4_EQ_ASYNC] = DRV_NAME " (async)" }; - err = mlx4_create_eq(dev, 1, MLX4_EQ_CATAS, - &priv->eq_table.eq[MLX4_EQ_CATAS]); - if (err) - goto err_out_async; - - for (i = 0; i < MLX4_EQ_CATAS; ++i) { + for (i = 0; i < MLX4_NUM_EQ; ++i) { err = request_irq(priv->eq_table.eq[i].irq, mlx4_msi_x_interrupt, 0, eq_name[i], priv->eq_table.eq + i); if (err) - goto err_out_catas; + goto err_out_async; priv->eq_table.eq[i].have_irq = 1; } - err = request_irq(priv->eq_table.eq[MLX4_EQ_CATAS].irq, - mlx4_catas_interrupt, 0, - eq_name[MLX4_EQ_CATAS], dev); - if (err) - goto err_out_catas; - - priv->eq_table.eq[MLX4_EQ_CATAS].have_irq = 1; } else { err = request_irq(dev->pdev->irq, mlx4_interrupt, IRQF_SHARED, DRV_NAME, dev); @@ -639,22 +614,11 @@ int __devinit mlx4_init_eq_table(struct mlx4_dev *dev) mlx4_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", priv->eq_table.eq[MLX4_EQ_ASYNC].eqn, err); - for (i = 0; i < MLX4_EQ_CATAS; ++i) + for (i = 0; i < MLX4_NUM_EQ; ++i) eq_set_ci(&priv->eq_table.eq[i], 1); - if (dev->flags & MLX4_FLAG_MSI_X) { - err = mlx4_MAP_EQ(dev, MLX4_CATAS_EVENT_MASK, 0, - priv->eq_table.eq[MLX4_EQ_CATAS].eqn); - if (err) - mlx4_warn(dev, "MAP_EQ for catas EQ %d failed (%d)\n", - priv->eq_table.eq[MLX4_EQ_CATAS].eqn, err); - } - return 0; -err_out_catas: - mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_CATAS]); - err_out_async: mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_ASYNC]); @@ -675,19 +639,13 @@ void mlx4_cleanup_eq_table(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); int i; - if (dev->flags & MLX4_FLAG_MSI_X) - mlx4_MAP_EQ(dev, MLX4_CATAS_EVENT_MASK, 1, - priv->eq_table.eq[MLX4_EQ_CATAS].eqn); - mlx4_MAP_EQ(dev, MLX4_ASYNC_EVENT_MASK, 1, priv->eq_table.eq[MLX4_EQ_ASYNC].eqn); mlx4_free_irqs(dev); - for (i = 0; i < MLX4_EQ_CATAS; ++i) + for (i = 0; i < MLX4_NUM_EQ; ++i) mlx4_free_eq(dev, &priv->eq_table.eq[i]); - if (dev->flags & MLX4_FLAG_MSI_X) - mlx4_free_eq(dev, &priv->eq_table.eq[MLX4_EQ_CATAS]); mlx4_unmap_clr_int(dev); diff --git a/drivers/net/mlx4/intf.c b/drivers/net/mlx4/intf.c index 9ae951bf6aa..be5d9e90ccf 100644 --- a/drivers/net/mlx4/intf.c +++ b/drivers/net/mlx4/intf.c @@ -142,6 +142,7 @@ int mlx4_register_device(struct mlx4_dev *dev) mlx4_add_device(intf, priv); mutex_unlock(&intf_mutex); + mlx4_start_catas_poll(dev); return 0; } @@ -151,6 +152,7 @@ void mlx4_unregister_device(struct mlx4_dev *dev) struct mlx4_priv *priv = mlx4_priv(dev); struct mlx4_interface *intf; + mlx4_stop_catas_poll(dev); mutex_lock(&intf_mutex); list_for_each_entry(intf, &intf_list, list) diff --git a/drivers/net/mlx4/main.c b/drivers/net/mlx4/main.c index a4f2e0475a7..4dc9dc19b71 100644 --- a/drivers/net/mlx4/main.c +++ b/drivers/net/mlx4/main.c @@ -78,7 +78,7 @@ static const char mlx4_version[] __devinitdata = static struct mlx4_profile default_profile = { .num_qp = 1 << 16, .num_srq = 1 << 16, - .rdmarc_per_qp = 4, + .rdmarc_per_qp = 1 << 4, .num_cq = 1 << 16, .num_mcg = 1 << 13, .num_mpt = 1 << 17, @@ -583,13 +583,11 @@ static int __devinit mlx4_setup_hca(struct mlx4_dev *dev) goto err_pd_table_free; } - mlx4_map_catas_buf(dev); - err = mlx4_init_eq_table(dev); if (err) { mlx4_err(dev, "Failed to initialize " "event queue table, aborting.\n"); - goto err_catas_buf; + goto err_mr_table_free; } err = mlx4_cmd_use_events(dev); @@ -659,8 +657,7 @@ err_cmd_poll: err_eq_table_free: mlx4_cleanup_eq_table(dev); -err_catas_buf: - mlx4_unmap_catas_buf(dev); +err_mr_table_free: mlx4_cleanup_mr_table(dev); err_pd_table_free: @@ -836,9 +833,6 @@ err_cleanup: mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); - - mlx4_unmap_catas_buf(dev); - mlx4_cleanup_mr_table(dev); mlx4_cleanup_pd_table(dev); mlx4_cleanup_uar_table(dev); @@ -885,9 +879,6 @@ static void __devexit mlx4_remove_one(struct pci_dev *pdev) mlx4_cleanup_cq_table(dev); mlx4_cmd_use_polling(dev); mlx4_cleanup_eq_table(dev); - - mlx4_unmap_catas_buf(dev); - mlx4_cleanup_mr_table(dev); mlx4_cleanup_pd_table(dev); @@ -908,6 +899,12 @@ static void __devexit mlx4_remove_one(struct pci_dev *pdev) } } +int mlx4_restart_one(struct pci_dev *pdev) +{ + mlx4_remove_one(pdev); + return mlx4_init_one(pdev, NULL); +} + static struct pci_device_id mlx4_pci_table[] = { { PCI_VDEVICE(MELLANOX, 0x6340) }, /* MT25408 "Hermon" SDR */ { PCI_VDEVICE(MELLANOX, 0x634a) }, /* MT25408 "Hermon" DDR */ @@ -930,6 +927,10 @@ static int __init mlx4_init(void) { int ret; + ret = mlx4_catas_init(); + if (ret) + return ret; + ret = pci_register_driver(&mlx4_driver); return ret < 0 ? ret : 0; } @@ -937,6 +938,7 @@ static int __init mlx4_init(void) static void __exit mlx4_cleanup(void) { pci_unregister_driver(&mlx4_driver); + mlx4_catas_cleanup(); } module_init(mlx4_init); diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index d9c91a71fc8..be304a7c2c9 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -39,6 +39,7 @@ #include <linux/mutex.h> #include <linux/radix-tree.h> +#include <linux/timer.h> #include <linux/mlx4/device.h> #include <linux/mlx4/doorbell.h> @@ -67,7 +68,6 @@ enum { enum { MLX4_EQ_ASYNC, MLX4_EQ_COMP, - MLX4_EQ_CATAS, MLX4_NUM_EQ }; @@ -248,7 +248,8 @@ struct mlx4_mcg_table { struct mlx4_catas_err { u32 __iomem *map; - int size; + struct timer_list timer; + struct list_head list; }; struct mlx4_priv { @@ -311,9 +312,11 @@ void mlx4_cleanup_qp_table(struct mlx4_dev *dev); void mlx4_cleanup_srq_table(struct mlx4_dev *dev); void mlx4_cleanup_mcg_table(struct mlx4_dev *dev); -void mlx4_map_catas_buf(struct mlx4_dev *dev); -void mlx4_unmap_catas_buf(struct mlx4_dev *dev); - +void mlx4_start_catas_poll(struct mlx4_dev *dev); +void mlx4_stop_catas_poll(struct mlx4_dev *dev); +int mlx4_catas_init(void); +void mlx4_catas_cleanup(void); +int mlx4_restart_one(struct pci_dev *pdev); int mlx4_register_device(struct mlx4_dev *dev); void mlx4_unregister_device(struct mlx4_dev *dev); void mlx4_dispatch_event(struct mlx4_dev *dev, enum mlx4_event type, diff --git a/drivers/pcmcia/ds.c b/drivers/pcmcia/ds.c index 143c6efc478..a99607142fc 100644 --- a/drivers/pcmcia/ds.c +++ b/drivers/pcmcia/ds.c @@ -1127,6 +1127,34 @@ static int pcmcia_bus_uevent(struct device *dev, char **envp, int num_envp, #endif +/************************ runtime PM support ***************************/ + +static int pcmcia_dev_suspend(struct device *dev, pm_message_t state); +static int pcmcia_dev_resume(struct device *dev); + +static int runtime_suspend(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_suspend(dev, PMSG_SUSPEND); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_SUSPEND; + return rc; +} + +static void runtime_resume(struct device *dev) +{ + int rc; + + down(&dev->sem); + rc = pcmcia_dev_resume(dev); + up(&dev->sem); + if (!rc) + dev->power.power_state.event = PM_EVENT_ON; +} + /************************ per-device sysfs output ***************************/ #define pcmcia_device_attr(field, test, format) \ @@ -1173,9 +1201,9 @@ static ssize_t pcmcia_store_pm_state(struct device *dev, struct device_attribute return -EINVAL; if ((!p_dev->suspended) && !strncmp(buf, "off", 3)) - ret = dpm_runtime_suspend(dev, PMSG_SUSPEND); + ret = runtime_suspend(dev); else if (p_dev->suspended && !strncmp(buf, "on", 2)) - dpm_runtime_resume(dev); + runtime_resume(dev); return ret ? ret : count; } @@ -1312,10 +1340,10 @@ static int pcmcia_bus_suspend_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || p_dev->suspended) return 0; - return dpm_runtime_suspend(dev, PMSG_SUSPEND); + return runtime_suspend(dev); } static int pcmcia_bus_resume_callback(struct device *dev, void * _data) @@ -1323,10 +1351,10 @@ static int pcmcia_bus_resume_callback(struct device *dev, void * _data) struct pcmcia_socket *skt = _data; struct pcmcia_device *p_dev = to_pcmcia_dev(dev); - if (p_dev->socket != skt) + if (p_dev->socket != skt || !p_dev->suspended) return 0; - dpm_runtime_resume(dev); + runtime_resume(dev); return 0; } diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig new file mode 100644 index 00000000000..b778ed71f63 --- /dev/null +++ b/drivers/uio/Kconfig @@ -0,0 +1,29 @@ +menu "Userspace I/O" + depends on !S390 + +config UIO + tristate "Userspace I/O drivers" + default n + help + Enable this to allow the userspace driver core code to be + built. This code allows userspace programs easy access to + kernel interrupts and memory locations, allowing some drivers + to be written in userspace. Note that a small kernel driver + is also required for interrupt handling to work properly. + + If you don't know what to do here, say N. + +config UIO_CIF + tristate "generic Hilscher CIF Card driver" + depends on UIO && PCI + default n + help + Driver for Hilscher CIF DeviceNet and Profibus cards. This + driver requires a userspace component that handles all of the + heavy lifting and can be found at: + http://www.osadl.org/projects/downloads/UIO/user/cif-* + + To compile this driver as a module, choose M here: the module + will be called uio_cif. + +endmenu diff --git a/drivers/uio/Makefile b/drivers/uio/Makefile new file mode 100644 index 00000000000..7fecfb459da --- /dev/null +++ b/drivers/uio/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_UIO) += uio.o +obj-$(CONFIG_UIO_CIF) += uio_cif.o diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c new file mode 100644 index 00000000000..865f32b63b5 --- /dev/null +++ b/drivers/uio/uio.c @@ -0,0 +1,701 @@ +/* + * drivers/uio/uio.c + * + * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de> + * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de> + * Copyright(C) 2006, Hans J. Koch <hjk@linutronix.de> + * Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com> + * + * Userspace IO + * + * Base Functions + * + * Licensed under the GPLv2 only. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <linux/device.h> +#include <linux/mm.h> +#include <linux/idr.h> +#include <linux/string.h> +#include <linux/kobject.h> +#include <linux/uio_driver.h> + +#define UIO_MAX_DEVICES 255 + +struct uio_device { + struct module *owner; + struct device *dev; + int minor; + atomic_t event; + struct fasync_struct *async_queue; + wait_queue_head_t wait; + int vma_count; + struct uio_info *info; + struct kset map_attr_kset; +}; + +static int uio_major; +static DEFINE_IDR(uio_idr); +static struct file_operations uio_fops; + +/* UIO class infrastructure */ +static struct uio_class { + struct kref kref; + struct class *class; +} *uio_class; + +/* + * attributes + */ + +static struct attribute attr_addr = { + .name = "addr", + .mode = S_IRUGO, +}; + +static struct attribute attr_size = { + .name = "size", + .mode = S_IRUGO, +}; + +static struct attribute* map_attrs[] = { + &attr_addr, &attr_size, NULL +}; + +static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct uio_mem *mem = container_of(kobj, struct uio_mem, kobj); + + if (strncmp(attr->name,"addr",4) == 0) + return sprintf(buf, "0x%lx\n", mem->addr); + + if (strncmp(attr->name,"size",4) == 0) + return sprintf(buf, "0x%lx\n", mem->size); + + return -ENODEV; +} + +static void map_attr_release(struct kobject *kobj) +{ + /* TODO ??? */ +} + +static struct sysfs_ops map_attr_ops = { + .show = map_attr_show, +}; + +static struct kobj_type map_attr_type = { + .release = map_attr_release, + .sysfs_ops = &map_attr_ops, + .default_attrs = map_attrs, +}; + +static ssize_t show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uio_device *idev = dev_get_drvdata(dev); + if (idev) + return sprintf(buf, "%s\n", idev->info->name); + else + return -ENODEV; +} +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); + +static ssize_t show_version(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uio_device *idev = dev_get_drvdata(dev); + if (idev) + return sprintf(buf, "%s\n", idev->info->version); + else + return -ENODEV; +} +static DEVICE_ATTR(version, S_IRUGO, show_version, NULL); + +static ssize_t show_event(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct uio_device *idev = dev_get_drvdata(dev); + if (idev) + return sprintf(buf, "%u\n", + (unsigned int)atomic_read(&idev->event)); + else + return -ENODEV; +} +static DEVICE_ATTR(event, S_IRUGO, show_event, NULL); + +static struct attribute *uio_attrs[] = { + &dev_attr_name.attr, + &dev_attr_version.attr, + &dev_attr_event.attr, + NULL, +}; + +static struct attribute_group uio_attr_grp = { + .attrs = uio_attrs, +}; + +/* + * device functions + */ +static int uio_dev_add_attributes(struct uio_device *idev) +{ + int ret; + int mi; + int map_found = 0; + struct uio_mem *mem; + + ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp); + if (ret) + goto err_group; + + for (mi = 0; mi < MAX_UIO_MAPS; mi++) { + mem = &idev->info->mem[mi]; + if (mem->size == 0) + break; + if (!map_found) { + map_found = 1; + kobject_set_name(&idev->map_attr_kset.kobj,"maps"); + idev->map_attr_kset.ktype = &map_attr_type; + idev->map_attr_kset.kobj.parent = &idev->dev->kobj; + ret = kset_register(&idev->map_attr_kset); + if (ret) + goto err_remove_group; + } + kobject_init(&mem->kobj); + kobject_set_name(&mem->kobj,"map%d",mi); + mem->kobj.parent = &idev->map_attr_kset.kobj; + mem->kobj.kset = &idev->map_attr_kset; + ret = kobject_add(&mem->kobj); + if (ret) + goto err_remove_maps; + } + + return 0; + +err_remove_maps: + for (mi--; mi>=0; mi--) { + mem = &idev->info->mem[mi]; + kobject_unregister(&mem->kobj); + } + kset_unregister(&idev->map_attr_kset); /* Needed ? */ +err_remove_group: + sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp); +err_group: + dev_err(idev->dev, "error creating sysfs files (%d)\n", ret); + return ret; +} + +static void uio_dev_del_attributes(struct uio_device *idev) +{ + int mi; + struct uio_mem *mem; + for (mi = 0; mi < MAX_UIO_MAPS; mi++) { + mem = &idev->info->mem[mi]; + if (mem->size == 0) + break; + kobject_unregister(&mem->kobj); + } + kset_unregister(&idev->map_attr_kset); + sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp); +} + +static int uio_get_minor(struct uio_device *idev) +{ + static DEFINE_MUTEX(minor_lock); + int retval = -ENOMEM; + int id; + + mutex_lock(&minor_lock); + if (idr_pre_get(&uio_idr, GFP_KERNEL) == 0) + goto exit; + + retval = idr_get_new(&uio_idr, idev, &id); + if (retval < 0) { + if (retval == -EAGAIN) + retval = -ENOMEM; + goto exit; + } + idev->minor = id & MAX_ID_MASK; +exit: + mutex_unlock(&minor_lock); + return retval; +} + +static void uio_free_minor(struct uio_device *idev) +{ + idr_remove(&uio_idr, idev->minor); +} + +/** + * uio_event_notify - trigger an interrupt event + * @info: UIO device capabilities + */ +void uio_event_notify(struct uio_info *info) +{ + struct uio_device *idev = info->uio_dev; + + atomic_inc(&idev->event); + wake_up_interruptible(&idev->wait); + kill_fasync(&idev->async_queue, SIGIO, POLL_IN); +} +EXPORT_SYMBOL_GPL(uio_event_notify); + +/** + * uio_interrupt - hardware interrupt handler + * @irq: IRQ number, can be UIO_IRQ_CYCLIC for cyclic timer + * @dev_id: Pointer to the devices uio_device structure + */ +static irqreturn_t uio_interrupt(int irq, void *dev_id) +{ + struct uio_device *idev = (struct uio_device *)dev_id; + irqreturn_t ret = idev->info->handler(irq, idev->info); + + if (ret == IRQ_HANDLED) + uio_event_notify(idev->info); + + return ret; +} + +struct uio_listener { + struct uio_device *dev; + s32 event_count; +}; + +static int uio_open(struct inode *inode, struct file *filep) +{ + struct uio_device *idev; + struct uio_listener *listener; + int ret = 0; + + idev = idr_find(&uio_idr, iminor(inode)); + if (!idev) + return -ENODEV; + + listener = kmalloc(sizeof(*listener), GFP_KERNEL); + if (!listener) + return -ENOMEM; + + listener->dev = idev; + listener->event_count = atomic_read(&idev->event); + filep->private_data = listener; + + if (idev->info->open) { + if (!try_module_get(idev->owner)) + return -ENODEV; + ret = idev->info->open(idev->info, inode); + module_put(idev->owner); + } + + if (ret) + kfree(listener); + + return ret; +} + +static int uio_fasync(int fd, struct file *filep, int on) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + + return fasync_helper(fd, filep, on, &idev->async_queue); +} + +static int uio_release(struct inode *inode, struct file *filep) +{ + int ret = 0; + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + + if (idev->info->release) { + if (!try_module_get(idev->owner)) + return -ENODEV; + ret = idev->info->release(idev->info, inode); + module_put(idev->owner); + } + if (filep->f_flags & FASYNC) + ret = uio_fasync(-1, filep, 0); + kfree(listener); + return ret; +} + +static unsigned int uio_poll(struct file *filep, poll_table *wait) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + + if (idev->info->irq == UIO_IRQ_NONE) + return -EIO; + + poll_wait(filep, &idev->wait, wait); + if (listener->event_count != atomic_read(&idev->event)) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t uio_read(struct file *filep, char __user *buf, + size_t count, loff_t *ppos) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + DECLARE_WAITQUEUE(wait, current); + ssize_t retval; + s32 event_count; + + if (idev->info->irq == UIO_IRQ_NONE) + return -EIO; + + if (count != sizeof(s32)) + return -EINVAL; + + add_wait_queue(&idev->wait, &wait); + + do { + set_current_state(TASK_INTERRUPTIBLE); + + event_count = atomic_read(&idev->event); + if (event_count != listener->event_count) { + if (copy_to_user(buf, &event_count, count)) + retval = -EFAULT; + else { + listener->event_count = event_count; + retval = count; + } + break; + } + + if (filep->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } while (1); + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&idev->wait, &wait); + + return retval; +} + +static int uio_find_mem_index(struct vm_area_struct *vma) +{ + int mi; + struct uio_device *idev = vma->vm_private_data; + + for (mi = 0; mi < MAX_UIO_MAPS; mi++) { + if (idev->info->mem[mi].size == 0) + return -1; + if (vma->vm_pgoff == mi) + return mi; + } + return -1; +} + +static void uio_vma_open(struct vm_area_struct *vma) +{ + struct uio_device *idev = vma->vm_private_data; + idev->vma_count++; +} + +static void uio_vma_close(struct vm_area_struct *vma) +{ + struct uio_device *idev = vma->vm_private_data; + idev->vma_count--; +} + +static struct page *uio_vma_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + struct uio_device *idev = vma->vm_private_data; + struct page* page = NOPAGE_SIGBUS; + + int mi = uio_find_mem_index(vma); + if (mi < 0) + return page; + + if (idev->info->mem[mi].memtype == UIO_MEM_LOGICAL) + page = virt_to_page(idev->info->mem[mi].addr); + else + page = vmalloc_to_page((void*)idev->info->mem[mi].addr); + get_page(page); + if (type) + *type = VM_FAULT_MINOR; + return page; +} + +static struct vm_operations_struct uio_vm_ops = { + .open = uio_vma_open, + .close = uio_vma_close, + .nopage = uio_vma_nopage, +}; + +static int uio_mmap_physical(struct vm_area_struct *vma) +{ + struct uio_device *idev = vma->vm_private_data; + int mi = uio_find_mem_index(vma); + if (mi < 0) + return -EINVAL; + + vma->vm_flags |= VM_IO | VM_RESERVED; + + return remap_pfn_range(vma, + vma->vm_start, + idev->info->mem[mi].addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + +static int uio_mmap_logical(struct vm_area_struct *vma) +{ + vma->vm_flags |= VM_RESERVED; + vma->vm_ops = &uio_vm_ops; + uio_vma_open(vma); + return 0; +} + +static int uio_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct uio_listener *listener = filep->private_data; + struct uio_device *idev = listener->dev; + int mi; + unsigned long requested_pages, actual_pages; + int ret = 0; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + vma->vm_private_data = idev; + + mi = uio_find_mem_index(vma); + if (mi < 0) + return -EINVAL; + + requested_pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; + actual_pages = (idev->info->mem[mi].size + PAGE_SIZE -1) >> PAGE_SHIFT; + if (requested_pages > actual_pages) + return -EINVAL; + + if (idev->info->mmap) { + if (!try_module_get(idev->owner)) + return -ENODEV; + ret = idev->info->mmap(idev->info, vma); + module_put(idev->owner); + return ret; + } + + switch (idev->info->mem[mi].memtype) { + case UIO_MEM_PHYS: + return uio_mmap_physical(vma); + case UIO_MEM_LOGICAL: + case UIO_MEM_VIRTUAL: + return uio_mmap_logical(vma); + default: + return -EINVAL; + } +} + +static struct file_operations uio_fops = { + .owner = THIS_MODULE, + .open = uio_open, + .release = uio_release, + .read = uio_read, + .mmap = uio_mmap, + .poll = uio_poll, + .fasync = uio_fasync, +}; + +static int uio_major_init(void) +{ + uio_major = register_chrdev(0, "uio", &uio_fops); + if (uio_major < 0) + return uio_major; + return 0; +} + +static void uio_major_cleanup(void) +{ + unregister_chrdev(uio_major, "uio"); +} + +static int init_uio_class(void) +{ + int ret = 0; + + if (uio_class != NULL) { + kref_get(&uio_class->kref); + goto exit; + } + + /* This is the first time in here, set everything up properly */ + ret = uio_major_init(); + if (ret) + goto exit; + + uio_class = kzalloc(sizeof(*uio_class), GFP_KERNEL); + if (!uio_class) { + ret = -ENOMEM; + goto err_kzalloc; + } + + kref_init(&uio_class->kref); + uio_class->class = class_create(THIS_MODULE, "uio"); + if (IS_ERR(uio_class->class)) { + ret = IS_ERR(uio_class->class); + printk(KERN_ERR "class_create failed for uio\n"); + goto err_class_create; + } + return 0; + +err_class_create: + kfree(uio_class); + uio_class = NULL; +err_kzalloc: + uio_major_cleanup(); +exit: + return ret; +} + +static void release_uio_class(struct kref *kref) +{ + /* Ok, we cheat as we know we only have one uio_class */ + class_destroy(uio_class->class); + kfree(uio_class); + uio_major_cleanup(); + uio_class = NULL; +} + +static void uio_class_destroy(void) +{ + if (uio_class) + kref_put(&uio_class->kref, release_uio_class); +} + +/** + * uio_register_device - register a new userspace IO device + * @owner: module that creates the new device + * @parent: parent device + * @info: UIO device capabilities + * + * returns zero on success or a negative error code. + */ +int __uio_register_device(struct module *owner, + struct device *parent, + struct uio_info *info) +{ + struct uio_device *idev; + int ret = 0; + + if (!parent || !info || !info->name || !info->version) + return -EINVAL; + + info->uio_dev = NULL; + + ret = init_uio_class(); + if (ret) + return ret; + + idev = kzalloc(sizeof(*idev), GFP_KERNEL); + if (!idev) { + ret = -ENOMEM; + goto err_kzalloc; + } + + idev->owner = owner; + idev->info = info; + init_waitqueue_head(&idev->wait); + atomic_set(&idev->event, 0); + + ret = uio_get_minor(idev); + if (ret) + goto err_get_minor; + + idev->dev = device_create(uio_class->class, parent, + MKDEV(uio_major, idev->minor), + "uio%d", idev->minor); + if (IS_ERR(idev->dev)) { + printk(KERN_ERR "UIO: device register failed\n"); + ret = PTR_ERR(idev->dev); + goto err_device_create; + } + dev_set_drvdata(idev->dev, idev); + + ret = uio_dev_add_attributes(idev); + if (ret) + goto err_uio_dev_add_attributes; + + info->uio_dev = idev; + + if (idev->info->irq >= 0) { + ret = request_irq(idev->info->irq, uio_interrupt, + idev->info->irq_flags, idev->info->name, idev); + if (ret) + goto err_request_irq; + } + + return 0; + +err_request_irq: + uio_dev_del_attributes(idev); +err_uio_dev_add_attributes: + device_destroy(uio_class->class, MKDEV(uio_major, idev->minor)); +err_device_create: + uio_free_minor(idev); +err_get_minor: + kfree(idev); +err_kzalloc: + uio_class_destroy(); + return ret; +} +EXPORT_SYMBOL_GPL(__uio_register_device); + +/** + * uio_unregister_device - unregister a industrial IO device + * @info: UIO device capabilities + * + */ +void uio_unregister_device(struct uio_info *info) +{ + struct uio_device *idev; + + if (!info || !info->uio_dev) + return; + + idev = info->uio_dev; + + uio_free_minor(idev); + + if (info->irq >= 0) + free_irq(info->irq, idev); + + uio_dev_del_attributes(idev); + + dev_set_drvdata(idev->dev, NULL); + device_destroy(uio_class->class, MKDEV(uio_major, idev->minor)); + kfree(idev); + uio_class_destroy(); + + return; +} +EXPORT_SYMBOL_GPL(uio_unregister_device); + +static int __init uio_init(void) +{ + return 0; +} + +static void __exit uio_exit(void) +{ +} + +module_init(uio_init) +module_exit(uio_exit) +MODULE_LICENSE("GPL v2"); diff --git a/drivers/uio/uio_cif.c b/drivers/uio/uio_cif.c new file mode 100644 index 00000000000..838bae46083 --- /dev/null +++ b/drivers/uio/uio_cif.c @@ -0,0 +1,156 @@ +/* + * UIO Hilscher CIF card driver + * + * (C) 2007 Hans J. Koch <hjk@linutronix.de> + * Original code (C) 2005 Benedikt Spranger <b.spranger@linutronix.de> + * + * Licensed under GPL version 2 only. + * + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/uio_driver.h> + +#include <asm/io.h> + +#ifndef PCI_DEVICE_ID_PLX_9030 +#define PCI_DEVICE_ID_PLX_9030 0x9030 +#endif + +#define PLX9030_INTCSR 0x4C +#define INTSCR_INT1_ENABLE 0x01 +#define INTSCR_INT1_STATUS 0x04 +#define INT1_ENABLED_AND_ACTIVE (INTSCR_INT1_ENABLE | INTSCR_INT1_STATUS) + +#define PCI_SUBVENDOR_ID_PEP 0x1518 +#define CIF_SUBDEVICE_PROFIBUS 0x430 +#define CIF_SUBDEVICE_DEVICENET 0x432 + + +static irqreturn_t hilscher_handler(int irq, struct uio_info *dev_info) +{ + void __iomem *plx_intscr = dev_info->mem[0].internal_addr + + PLX9030_INTCSR; + + if ((ioread8(plx_intscr) & INT1_ENABLED_AND_ACTIVE) + != INT1_ENABLED_AND_ACTIVE) + return IRQ_NONE; + + /* Disable interrupt */ + iowrite8(ioread8(plx_intscr) & ~INTSCR_INT1_ENABLE, plx_intscr); + return IRQ_HANDLED; +} + +static int __devinit hilscher_pci_probe(struct pci_dev *dev, + const struct pci_device_id *id) +{ + struct uio_info *info; + + info = kzalloc(sizeof(struct uio_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + if (pci_enable_device(dev)) + goto out_free; + + if (pci_request_regions(dev, "hilscher")) + goto out_disable; + + info->mem[0].addr = pci_resource_start(dev, 0); + if (!info->mem[0].addr) + goto out_release; + info->mem[0].internal_addr = ioremap(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + if (!info->mem[0].internal_addr) + goto out_release; + + info->mem[0].size = pci_resource_len(dev, 0); + info->mem[0].memtype = UIO_MEM_PHYS; + info->mem[1].addr = pci_resource_start(dev, 2); + info->mem[1].size = pci_resource_len(dev, 2); + info->mem[1].memtype = UIO_MEM_PHYS; + switch (id->subdevice) { + case CIF_SUBDEVICE_PROFIBUS: + info->name = "CIF_Profibus"; + break; + case CIF_SUBDEVICE_DEVICENET: + info->name = "CIF_Devicenet"; + break; + default: + info->name = "CIF_???"; + } + info->version = "0.0.1"; + info->irq = dev->irq; + info->irq_flags = IRQF_DISABLED | IRQF_SHARED; + info->handler = hilscher_handler; + + if (uio_register_device(&dev->dev, info)) + goto out_unmap; + + pci_set_drvdata(dev, info); + + return 0; +out_unmap: + iounmap(info->mem[0].internal_addr); +out_release: + pci_release_regions(dev); +out_disable: + pci_disable_device(dev); +out_free: + kfree (info); + return -ENODEV; +} + +static void hilscher_pci_remove(struct pci_dev *dev) +{ + struct uio_info *info = pci_get_drvdata(dev); + + uio_unregister_device(info); + pci_release_regions(dev); + pci_disable_device(dev); + pci_set_drvdata(dev, NULL); + iounmap(info->mem[0].internal_addr); + + kfree (info); +} + +static struct pci_device_id hilscher_pci_ids[] = { + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9030, + .subvendor = PCI_SUBVENDOR_ID_PEP, + .subdevice = CIF_SUBDEVICE_PROFIBUS, + }, + { + .vendor = PCI_VENDOR_ID_PLX, + .device = PCI_DEVICE_ID_PLX_9030, + .subvendor = PCI_SUBVENDOR_ID_PEP, + .subdevice = CIF_SUBDEVICE_DEVICENET, + }, + { 0, } +}; + +static struct pci_driver hilscher_pci_driver = { + .name = "hilscher", + .id_table = hilscher_pci_ids, + .probe = hilscher_pci_probe, + .remove = hilscher_pci_remove, +}; + +static int __init hilscher_init_module(void) +{ + return pci_register_driver(&hilscher_pci_driver); +} + +static void __exit hilscher_exit_module(void) +{ + pci_unregister_driver(&hilscher_pci_driver); +} + +module_init(hilscher_init_module); +module_exit(hilscher_exit_module); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Hans J. Koch, Benedikt Spranger"); diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index 73c49362cd4..654857493a8 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -29,13 +29,6 @@ #include "hcd.h" #include "usb.h" -#define VERBOSE_DEBUG 0 - -#if VERBOSE_DEBUG -#define dev_vdbg dev_dbg -#else -#define dev_vdbg(dev, fmt, args...) do { } while (0) -#endif #ifdef CONFIG_HOTPLUG diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 1d533a2ec3a..11be8a325e2 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -345,11 +345,6 @@ void debugfs_remove(struct dentry *dentry) switch (dentry->d_inode->i_mode & S_IFMT) { case S_IFDIR: ret = simple_rmdir(parent->d_inode, dentry); - if (ret) - printk(KERN_ERR - "DebugFS rmdir on %s failed : " - "directory not empty.\n", - dentry->d_name.name); break; case S_IFLNK: kfree(dentry->d_inode->i_private); diff --git a/fs/gfs2/ops_file.c b/fs/gfs2/ops_file.c index 196d83266e3..1a5e8e893d7 100644 --- a/fs/gfs2/ops_file.c +++ b/fs/gfs2/ops_file.c @@ -489,6 +489,29 @@ static int gfs2_fsync(struct file *file, struct dentry *dentry, int datasync) } /** + * gfs2_setlease - acquire/release a file lease + * @file: the file pointer + * @arg: lease type + * @fl: file lock + * + * Returns: errno + */ + +static int gfs2_setlease(struct file *file, long arg, struct file_lock **fl) +{ + struct gfs2_sbd *sdp = GFS2_SB(file->f_mapping->host); + + /* + * We don't currently have a way to enforce a lease across the whole + * cluster; until we do, disable leases (by just returning -EINVAL), + * unless the administrator has requested purely local locking. + */ + if (!sdp->sd_args.ar_localflocks) + return -EINVAL; + return setlease(file, arg, fl); +} + +/** * gfs2_lock - acquire/release a posix lock on a file * @file: the file pointer * @cmd: either modify or retrieve lock state, possibly wait @@ -638,6 +661,7 @@ const struct file_operations gfs2_file_fops = { .flock = gfs2_flock, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, + .setlease = gfs2_setlease, }; const struct file_operations gfs2_dir_fops = { diff --git a/fs/locks.c b/fs/locks.c index 431a8b871fc..4f2d749ac62 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -458,22 +458,20 @@ static int lease_init(struct file *filp, int type, struct file_lock *fl) } /* Allocate a file_lock initialised to this type of lease */ -static int lease_alloc(struct file *filp, int type, struct file_lock **flp) +static struct file_lock *lease_alloc(struct file *filp, int type) { struct file_lock *fl = locks_alloc_lock(); int error = -ENOMEM; if (fl == NULL) - goto out; + return ERR_PTR(error); error = lease_init(filp, type, fl); if (error) { locks_free_lock(fl); - fl = NULL; + return ERR_PTR(error); } -out: - *flp = fl; - return error; + return fl; } /* Check if two locks overlap each other. @@ -661,7 +659,7 @@ static int locks_block_on_timeout(struct file_lock *blocker, struct file_lock *w return result; } -int +void posix_test_lock(struct file *filp, struct file_lock *fl) { struct file_lock *cfl; @@ -673,14 +671,12 @@ posix_test_lock(struct file *filp, struct file_lock *fl) if (posix_locks_conflict(cfl, fl)) break; } - if (cfl) { + if (cfl) __locks_copy_lock(fl, cfl); - unlock_kernel(); - return 1; - } else + else fl->fl_type = F_UNLCK; unlock_kernel(); - return 0; + return; } EXPORT_SYMBOL(posix_test_lock); @@ -1169,9 +1165,9 @@ static void time_out_leases(struct inode *inode) * @inode: the inode of the file to return * @mode: the open mode (read or write) * - * break_lease (inlined for speed) has checked there already - * is a lease on this file. Leases are broken on a call to open() - * or truncate(). This function can sleep unless you + * break_lease (inlined for speed) has checked there already is at least + * some kind of lock (maybe a lease) on this file. Leases are broken on + * a call to open() or truncate(). This function can sleep unless you * specified %O_NONBLOCK to your open(). */ int __break_lease(struct inode *inode, unsigned int mode) @@ -1179,12 +1175,10 @@ int __break_lease(struct inode *inode, unsigned int mode) int error = 0, future; struct file_lock *new_fl, *flock; struct file_lock *fl; - int alloc_err; unsigned long break_time; int i_have_this_lease = 0; - alloc_err = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK, - &new_fl); + new_fl = lease_alloc(NULL, mode & FMODE_WRITE ? F_WRLCK : F_RDLCK); lock_kernel(); @@ -1212,8 +1206,9 @@ int __break_lease(struct inode *inode, unsigned int mode) goto out; } - if (alloc_err && !i_have_this_lease && ((mode & O_NONBLOCK) == 0)) { - error = alloc_err; + if (IS_ERR(new_fl) && !i_have_this_lease + && ((mode & O_NONBLOCK) == 0)) { + error = PTR_ERR(new_fl); goto out; } @@ -1260,7 +1255,7 @@ restart: out: unlock_kernel(); - if (!alloc_err) + if (!IS_ERR(new_fl)) locks_free_lock(new_fl); return error; } @@ -1329,7 +1324,7 @@ int fcntl_getlease(struct file *filp) } /** - * __setlease - sets a lease on an open file + * setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain * @flp: input - file_lock to use, output - file_lock inserted @@ -1339,18 +1334,24 @@ int fcntl_getlease(struct file *filp) * * Called with kernel lock held. */ -static int __setlease(struct file *filp, long arg, struct file_lock **flp) +int setlease(struct file *filp, long arg, struct file_lock **flp) { struct file_lock *fl, **before, **my_before = NULL, *lease; struct dentry *dentry = filp->f_path.dentry; struct inode *inode = dentry->d_inode; int error, rdlease_count = 0, wrlease_count = 0; + if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) + return -EACCES; + if (!S_ISREG(inode->i_mode)) + return -EINVAL; + error = security_file_lock(filp, arg); + if (error) + return error; + time_out_leases(inode); - error = -EINVAL; - if (!flp || !(*flp) || !(*flp)->fl_lmops || !(*flp)->fl_lmops->fl_break) - goto out; + BUG_ON(!(*flp)->fl_lmops->fl_break); lease = *flp; @@ -1418,39 +1419,49 @@ static int __setlease(struct file *filp, long arg, struct file_lock **flp) out: return error; } +EXPORT_SYMBOL(setlease); /** - * setlease - sets a lease on an open file + * vfs_setlease - sets a lease on an open file * @filp: file pointer * @arg: type of lease to obtain * @lease: file_lock to use * * Call this to establish a lease on the file. - * The fl_lmops fl_break function is required by break_lease + * The (*lease)->fl_lmops->fl_break operation must be set; if not, + * break_lease will oops! + * + * This will call the filesystem's setlease file method, if + * defined. Note that there is no getlease method; instead, the + * filesystem setlease method should call back to setlease() to + * add a lease to the inode's lease list, where fcntl_getlease() can + * find it. Since fcntl_getlease() only reports whether the current + * task holds a lease, a cluster filesystem need only do this for + * leases held by processes on this node. + * + * There is also no break_lease method; filesystems that + * handle their own leases shoud break leases themselves from the + * filesystem's open, create, and (on truncate) setattr methods. + * + * Warning: the only current setlease methods exist only to disable + * leases in certain cases. More vfs changes may be required to + * allow a full filesystem lease implementation. */ -int setlease(struct file *filp, long arg, struct file_lock **lease) +int vfs_setlease(struct file *filp, long arg, struct file_lock **lease) { - struct dentry *dentry = filp->f_path.dentry; - struct inode *inode = dentry->d_inode; int error; - if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) - return -EACCES; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - error = security_file_lock(filp, arg); - if (error) - return error; - lock_kernel(); - error = __setlease(filp, arg, lease); + if (filp->f_op && filp->f_op->setlease) + error = filp->f_op->setlease(filp, arg, lease); + else + error = setlease(filp, arg, lease); unlock_kernel(); return error; } - -EXPORT_SYMBOL(setlease); +EXPORT_SYMBOL_GPL(vfs_setlease); /** * fcntl_setlease - sets a lease on an open file @@ -1469,14 +1480,6 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) struct inode *inode = dentry->d_inode; int error; - if ((current->fsuid != inode->i_uid) && !capable(CAP_LEASE)) - return -EACCES; - if (!S_ISREG(inode->i_mode)) - return -EINVAL; - error = security_file_lock(filp, arg); - if (error) - return error; - locks_init_lock(&fl); error = lease_init(filp, arg, &fl); if (error) @@ -1484,15 +1487,15 @@ int fcntl_setlease(unsigned int fd, struct file *filp, long arg) lock_kernel(); - error = __setlease(filp, arg, &flp); + error = vfs_setlease(filp, arg, &flp); if (error || arg == F_UNLCK) goto out_unlock; error = fasync_helper(fd, filp, 1, &flp->fl_fasync); if (error < 0) { - /* remove lease just inserted by __setlease */ + /* remove lease just inserted by setlease */ flp->fl_type = F_UNLCK | F_INPROGRESS; - flp->fl_break_time = jiffies- 10; + flp->fl_break_time = jiffies - 10; time_out_leases(inode); goto out_unlock; } @@ -1597,8 +1600,7 @@ asmlinkage long sys_flock(unsigned int fd, unsigned int cmd) /** * vfs_test_lock - test file byte range lock * @filp: The file to test lock for - * @fl: The lock to test - * @conf: Place to return a copy of the conflicting lock, if found + * @fl: The lock to test; also used to hold result * * Returns -ERRNO on failure. Indicates presence of conflicting lock by * setting conf->fl_type to something other than F_UNLCK. diff --git a/fs/nfs/file.c b/fs/nfs/file.c index 8689b736fdd..c87dc713b5d 100644 --- a/fs/nfs/file.c +++ b/fs/nfs/file.c @@ -53,6 +53,7 @@ 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); static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl); +static int nfs_setlease(struct file *file, long arg, struct file_lock **fl); const struct file_operations nfs_file_operations = { .llseek = nfs_file_llseek, @@ -69,6 +70,7 @@ const struct file_operations nfs_file_operations = { .flock = nfs_flock, .splice_read = nfs_file_splice_read, .check_flags = nfs_check_flags, + .setlease = nfs_setlease, }; const struct inode_operations nfs_file_inode_operations = { @@ -400,7 +402,9 @@ static int do_getlk(struct file *filp, int cmd, struct file_lock *fl) lock_kernel(); /* Try local locking first */ - if (posix_test_lock(filp, fl)) { + posix_test_lock(filp, fl); + if (fl->fl_type != F_UNLCK) { + /* found a conflict */ goto out; } @@ -558,3 +562,13 @@ static int nfs_flock(struct file *filp, int cmd, struct file_lock *fl) return do_unlk(filp, cmd, fl); return do_setlk(filp, cmd, fl); } + +static int nfs_setlease(struct file *file, long arg, struct file_lock **fl) +{ + /* + * There is no protocol support for leases, so we have no way + * to implement them correctly in the face of opens by other + * clients. + */ + return -EINVAL; +} diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index e4a4c87ec8c..6284807bd37 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c @@ -256,7 +256,7 @@ nfs4_close_delegation(struct nfs4_delegation *dp) /* The following nfsd_close may not actually close the file, * but we want to remove the lease in any case. */ if (dp->dl_flock) - setlease(filp, F_UNLCK, &dp->dl_flock); + vfs_setlease(filp, F_UNLCK, &dp->dl_flock); nfsd_close(filp); } @@ -1402,7 +1402,7 @@ void nfsd_release_deleg_cb(struct file_lock *fl) /* * Set the delegation file_lock back pointer. * - * Called from __setlease() with lock_kernel() held. + * Called from setlease() with lock_kernel() held. */ static void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) @@ -1416,7 +1416,7 @@ void nfsd_copy_lock_deleg_cb(struct file_lock *new, struct file_lock *fl) } /* - * Called from __setlease() with lock_kernel() held + * Called from setlease() with lock_kernel() held */ static int nfsd_same_client_deleg_cb(struct file_lock *onlist, struct file_lock *try) @@ -1716,10 +1716,10 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta fl.fl_file = stp->st_vfs_file; fl.fl_pid = current->tgid; - /* setlease checks to see if delegation should be handed out. + /* vfs_setlease checks to see if delegation should be handed out. * the lock_manager callbacks fl_mylease and fl_change are used */ - if ((status = setlease(stp->st_vfs_file, + if ((status = vfs_setlease(stp->st_vfs_file, flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK, &flp))) { dprintk("NFSD: setlease failed [%d], no delegation\n", status); unhash_delegation(dp); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index aee966c44aa..048e6054c2f 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -361,20 +361,20 @@ static struct dentry_operations sysfs_dentry_ops = { struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) { char *dup_name = NULL; - struct sysfs_dirent *sd = NULL; + struct sysfs_dirent *sd; if (type & SYSFS_COPY_NAME) { name = dup_name = kstrdup(name, GFP_KERNEL); if (!name) - goto err_out; + return NULL; } sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); if (!sd) - goto err_out; + goto err_out1; if (sysfs_alloc_ino(&sd->s_ino)) - goto err_out; + goto err_out2; atomic_set(&sd->s_count, 1); atomic_set(&sd->s_active, 0); @@ -386,9 +386,10 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) return sd; - err_out: - kfree(dup_name); + err_out2: kmem_cache_free(sysfs_dir_cachep, sd); + err_out1: + kfree(dup_name); return NULL; } @@ -698,17 +699,19 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, /* link in */ sysfs_addrm_start(&acxt, parent_sd); + if (!sysfs_find_dirent(parent_sd, name)) { sysfs_add_one(&acxt, sd); sysfs_link_sibling(sd); } - if (sysfs_addrm_finish(&acxt)) { - *p_sd = sd; - return 0; + + if (!sysfs_addrm_finish(&acxt)) { + sysfs_put(sd); + return -EEXIST; } - sysfs_put(sd); - return -EEXIST; + *p_sd = sd; + return 0; } int sysfs_create_subdir(struct kobject *kobj, const char *name, diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index cc497994b2a..3e1cc062a74 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -410,11 +410,12 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, sysfs_link_sibling(sd); } - if (sysfs_addrm_finish(&acxt)) - return 0; + if (!sysfs_addrm_finish(&acxt)) { + sysfs_put(sd); + return -EEXIST; + } - sysfs_put(sd); - return -EEXIST; + return 0; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 3756e152285..10d1b52899f 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -133,7 +133,7 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) */ static struct lock_class_key sysfs_inode_imutex_key; -void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) +static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { inode->i_blocks = 0; inode->i_mapping->a_ops = &sysfs_aops; diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 402cc356203..60714d075c2 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -43,19 +43,19 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_gran = 1; sysfs_sb = sb; - inode = new_inode(sysfs_sb); + /* get root inode, initialize and unlock it */ + inode = sysfs_get_inode(&sysfs_root); if (!inode) { pr_debug("sysfs: could not get root inode\n"); return -ENOMEM; } - sysfs_init_inode(&sysfs_root, inode); - inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); + inc_nlink(inode); /* directory, account for "." */ + unlock_new_inode(inode); + /* instantiate and link root dentry */ root = d_alloc_root(inode); if (!root) { pr_debug("%s: could not get root dentry!\n",__FUNCTION__); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 2f86e042229..4ce687f0b5d 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -86,7 +86,9 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto out_put; + sd->s_elem.symlink.target_sd = target_sd; + target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt, parent_sd); @@ -95,11 +97,13 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char sysfs_link_sibling(sd); } - if (sysfs_addrm_finish(&acxt)) - return 0; + if (!sysfs_addrm_finish(&acxt)) { + error = -EEXIST; + goto out_put; + } + + return 0; - error = -EEXIST; - /* fall through */ out_put: sysfs_put(target_sd); sysfs_put(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6a37f2386a8..6b8c8d76d30 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -71,7 +71,6 @@ extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); extern void sysfs_delete_inode(struct inode *inode); -extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd); extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode); diff --git a/include/linux/device.h b/include/linux/device.h index be2debed70d..d9f0a57f5a2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -572,6 +572,16 @@ dev_dbg(struct device * dev, const char * fmt, ...) } #endif +#ifdef VERBOSE_DEBUG +#define dev_vdbg dev_dbg +#else +static inline int __attribute__ ((format (printf, 2, 3))) +dev_vdbg(struct device * dev, const char * fmt, ...) +{ + return 0; +} +#endif + #define dev_err(dev, format, arg...) \ dev_printk(KERN_ERR , dev , format , ## arg) #define dev_info(dev, format, arg...) \ diff --git a/include/linux/fs.h b/include/linux/fs.h index 0b806c5e32e..9562a59b370 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -862,7 +862,7 @@ extern void locks_init_lock(struct file_lock *); extern void locks_copy_lock(struct file_lock *, struct file_lock *); extern void locks_remove_posix(struct file *, fl_owner_t); extern void locks_remove_flock(struct file *); -extern int posix_test_lock(struct file *, struct file_lock *); +extern void posix_test_lock(struct file *, struct file_lock *); extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *); extern int posix_lock_file_wait(struct file *, struct file_lock *); extern int posix_unblock_lock(struct file *, struct file_lock *); @@ -873,6 +873,7 @@ 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); extern int setlease(struct file *, long, struct file_lock **); +extern int vfs_setlease(struct file *, long, struct file_lock **); 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); @@ -1122,6 +1123,7 @@ struct file_operations { int (*flock) (struct file *, int, struct file_lock *); ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); + int (*setlease)(struct file *, long, struct file_lock **); }; struct inode_operations { diff --git a/include/linux/kobject.h b/include/linux/kobject.h index 06cbf41d32d..aa2fe22b1ba 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -36,15 +36,24 @@ extern char uevent_helper[]; /* counter to tag the uevent, read only except for the kobject core */ extern u64 uevent_seqnum; -/* the actions here must match the proper string in lib/kobject_uevent.c */ -typedef int __bitwise kobject_action_t; +/* + * The actions here must match the index to the string array + * in lib/kobject_uevent.c + * + * Do not add new actions here without checking with the driver-core + * maintainers. Action strings are not meant to express subsystem + * or device specific properties. In most cases you want to send a + * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event + * specific variables added to the event environment. + */ enum kobject_action { - KOBJ_ADD = (__force kobject_action_t) 0x01, /* exclusive to core */ - KOBJ_REMOVE = (__force kobject_action_t) 0x02, /* exclusive to core */ - KOBJ_CHANGE = (__force kobject_action_t) 0x03, /* device state change */ - KOBJ_OFFLINE = (__force kobject_action_t) 0x04, /* device offline */ - KOBJ_ONLINE = (__force kobject_action_t) 0x05, /* device online */ - KOBJ_MOVE = (__force kobject_action_t) 0x06, /* device move */ + KOBJ_ADD, + KOBJ_REMOVE, + KOBJ_CHANGE, + KOBJ_MOVE, + KOBJ_ONLINE, + KOBJ_OFFLINE, + KOBJ_MAX }; struct kobject { diff --git a/include/linux/pm.h b/include/linux/pm.h index 273781c82e4..2735b7cadd2 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -284,8 +284,6 @@ extern int device_prepare_suspend(pm_message_t state); #define device_may_wakeup(dev) \ (device_can_wakeup(dev) && (dev)->power.should_wakeup) -extern int dpm_runtime_suspend(struct device *, pm_message_t); -extern void dpm_runtime_resume(struct device *); extern void __suspend_report_result(const char *function, void *fn, int ret); #define suspend_report_result(fn, ret) \ @@ -317,15 +315,6 @@ static inline int device_suspend(pm_message_t state) #define device_set_wakeup_enable(dev,val) do{}while(0) #define device_may_wakeup(dev) (0) -static inline int dpm_runtime_suspend(struct device * dev, pm_message_t state) -{ - return 0; -} - -static inline void dpm_runtime_resume(struct device * dev) -{ -} - #define suspend_report_result(fn, ret) do { } while (0) static inline int call_platform_enable_wakeup(struct device *dev, int is_on) diff --git a/include/linux/uio_driver.h b/include/linux/uio_driver.h new file mode 100644 index 00000000000..44c28e94df5 --- /dev/null +++ b/include/linux/uio_driver.h @@ -0,0 +1,91 @@ +/* + * include/linux/uio_driver.h + * + * Copyright(C) 2005, Benedikt Spranger <b.spranger@linutronix.de> + * Copyright(C) 2005, Thomas Gleixner <tglx@linutronix.de> + * Copyright(C) 2006, Hans J. Koch <hjk@linutronix.de> + * Copyright(C) 2006, Greg Kroah-Hartman <greg@kroah.com> + * + * Userspace IO driver. + * + * Licensed under the GPLv2 only. + */ + +#ifndef _UIO_DRIVER_H_ +#define _UIO_DRIVER_H_ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/interrupt.h> + +/** + * struct uio_mem - description of a UIO memory region + * @kobj: kobject for this mapping + * @addr: address of the device's memory + * @size: size of IO + * @memtype: type of memory addr points to + * @internal_addr: ioremap-ped version of addr, for driver internal use + */ +struct uio_mem { + struct kobject kobj; + unsigned long addr; + unsigned long size; + int memtype; + void __iomem *internal_addr; +}; + +#define MAX_UIO_MAPS 5 + +struct uio_device; + +/** + * struct uio_info - UIO device capabilities + * @uio_dev: the UIO device this info belongs to + * @name: device name + * @version: device driver version + * @mem: list of mappable memory regions, size==0 for end of list + * @irq: interrupt number or UIO_IRQ_CUSTOM + * @irq_flags: flags for request_irq() + * @priv: optional private data + * @handler: the device's irq handler + * @mmap: mmap operation for this uio device + * @open: open operation for this uio device + * @release: release operation for this uio device + */ +struct uio_info { + struct uio_device *uio_dev; + char *name; + char *version; + struct uio_mem mem[MAX_UIO_MAPS]; + long irq; + unsigned long irq_flags; + void *priv; + irqreturn_t (*handler)(int irq, struct uio_info *dev_info); + int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); + int (*open)(struct uio_info *info, struct inode *inode); + int (*release)(struct uio_info *info, struct inode *inode); +}; + +extern int __must_check + __uio_register_device(struct module *owner, + struct device *parent, + struct uio_info *info); +static inline int __must_check + uio_register_device(struct device *parent, struct uio_info *info) +{ + return __uio_register_device(THIS_MODULE, parent, info); +} +extern void uio_unregister_device(struct uio_info *info); +extern void uio_event_notify(struct uio_info *info); + +/* defines for uio_device->irq */ +#define UIO_IRQ_CUSTOM -1 +#define UIO_IRQ_NONE -2 + +/* defines for uio_device->memtype */ +#define UIO_MEM_NONE 0 +#define UIO_MEM_PHYS 1 +#define UIO_MEM_LOGICAL 2 +#define UIO_MEM_VIRTUAL 3 + +#endif /* _LINUX_UIO_DRIVER_H_ */ diff --git a/kernel/power/Kconfig b/kernel/power/Kconfig index 495b7d4dd33..73328476761 100644 --- a/kernel/power/Kconfig +++ b/kernel/power/Kconfig @@ -65,18 +65,6 @@ config PM_TRACE CAUTION: this option will cause your machine's real-time clock to be set to an invalid time after a resume. -config PM_SYSFS_DEPRECATED - bool "Driver model /sys/devices/.../power/state files (DEPRECATED)" - depends on PM && SYSFS - default n - help - The driver model started out with a sysfs file intended to provide - a userspace hook for device power management. This feature has never - worked very well, except for limited testing purposes, and so it will - be removed. It's not clear that a generic mechanism could really - handle the wide variability of device power states; any replacements - are likely to be bus or driver specific. - config SOFTWARE_SUSPEND bool "Software Suspend (Hibernation)" depends on PM && SWAP && (((X86 || PPC64_SWSUSP) && (!SMP || SUSPEND_SMP)) || ((FRV || PPC32) && !SMP)) diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index bd5ecbbafab..6a80c784a8f 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -33,25 +33,15 @@ static DEFINE_SPINLOCK(sequence_lock); static struct sock *uevent_sock; #endif -static char *action_to_string(enum kobject_action action) -{ - switch (action) { - case KOBJ_ADD: - return "add"; - case KOBJ_REMOVE: - return "remove"; - case KOBJ_CHANGE: - return "change"; - case KOBJ_OFFLINE: - return "offline"; - case KOBJ_ONLINE: - return "online"; - case KOBJ_MOVE: - return "move"; - default: - return NULL; - } -} +/* the strings here must match the enum in include/linux/kobject.h */ +const char *kobject_actions[] = { + "add", + "remove", + "change", + "move", + "online", + "offline", +}; /** * kobject_uevent_env - send an uevent with environmental data @@ -83,7 +73,7 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, pr_debug("%s\n", __FUNCTION__); - action_string = action_to_string(action); + action_string = kobject_actions[action]; if (!action_string) { pr_debug("kobject attempted to send uevent without action_string!\n"); return -EINVAL; |