]> git.openfabrics.org - ~shefty/rdma-dev.git/commitdiff
Merge branch 'spi/next' of git://git.secretlab.ca/git/linux-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 Aug 2011 06:49:38 +0000 (20:49 -1000)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 3 Aug 2011 06:49:38 +0000 (20:49 -1000)
* 'spi/next' of git://git.secretlab.ca/git/linux-2.6:
  spi/pl022: remove function cannot exit

128 files changed:
Documentation/ABI/testing/pstore
Documentation/device-mapper/dm-crypt.txt
Documentation/device-mapper/dm-flakey.txt
Documentation/device-mapper/dm-raid.txt
Documentation/dmaengine.txt
Documentation/kernel-parameters.txt
arch/ia64/Kconfig
arch/ia64/include/asm/gpio.h [new file with mode: 0644]
drivers/acpi/apei/erst.c
drivers/base/devtmpfs.c
drivers/dma/TODO
drivers/dma/amba-pl08x.c
drivers/dma/at_hdmac.c
drivers/dma/coh901318.c
drivers/dma/dmaengine.c
drivers/dma/ep93xx_dma.c
drivers/dma/imx-sdma.c
drivers/dma/intel_mid_dma.c
drivers/dma/ipu/ipu_idmac.c
drivers/dma/mv_xor.c
drivers/dma/mxs-dma.c
drivers/dma/pch_dma.c
drivers/dma/pl330.c
drivers/dma/ste_dma40.c
drivers/dma/ste_dma40_ll.h
drivers/firmware/efivars.c
drivers/md/Kconfig
drivers/md/dm-crypt.c
drivers/md/dm-flakey.c
drivers/md/dm-io.c
drivers/md/dm-ioctl.c
drivers/md/dm-kcopyd.c
drivers/md/dm-log-userspace-base.c
drivers/md/dm-log.c
drivers/md/dm-mpath.c
drivers/md/dm-raid.c
drivers/md/dm-snap-persistent.c
drivers/md/dm-snap.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/dm.h
drivers/regulator/core.c
drivers/regulator/dummy.c
drivers/regulator/tps65910-regulator.c
drivers/regulator/twl-regulator.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-ldo.c
drivers/regulator/wm8994-regulator.c
drivers/watchdog/Kconfig
drivers/watchdog/nv_tco.c
drivers/watchdog/shwdt.c
fs/9p/acl.c
fs/9p/acl.h
fs/9p/vfs_inode_dotl.c
fs/block_dev.c
fs/btrfs/acl.c
fs/btrfs/inode.c
fs/dcache.c
fs/ext2/acl.c
fs/ext3/acl.c
fs/ext4/Makefile
fs/ext4/acl.c
fs/ext4/balloc.c
fs/ext4/block_validity.c
fs/ext4/ext4.h
fs/ext4/extents.c
fs/ext4/fsync.c
fs/ext4/ialloc.c
fs/ext4/indirect.c [new file with mode: 0644]
fs/ext4/inode.c
fs/ext4/ioctl.c
fs/ext4/mballoc.c
fs/ext4/mballoc.h
fs/ext4/namei.c
fs/ext4/page-io.c
fs/ext4/resize.c
fs/ext4/super.c
fs/ext4/truncate.h [new file with mode: 0644]
fs/generic_acl.c
fs/gfs2/acl.c
fs/hppfs/hppfs.c
fs/inode.c
fs/jbd2/checkpoint.c
fs/jbd2/journal.c
fs/jffs2/acl.c
fs/jffs2/acl.h
fs/jffs2/fs.c
fs/jffs2/os-linux.h
fs/jfs/acl.c
fs/jfs/xattr.c
fs/namei.c
fs/nfs/nfs3acl.c
fs/nfs/nfs3proc.c
fs/ocfs2/acl.c
fs/posix_acl.c
fs/pstore/inode.c
fs/pstore/internal.h
fs/pstore/platform.c
fs/reiserfs/xattr_acl.c
fs/xfs/linux-2.6/xfs_acl.c
include/linux/amba/pl08x.h
include/linux/device-mapper.h
include/linux/dm-ioctl.h
include/linux/dm-kcopyd.h
include/linux/efi.h
include/linux/fs.h
include/linux/jbd2.h
include/linux/nfs_fs.h
include/linux/posix_acl.h
include/linux/pstore.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/trace/events/ext4.h
include/trace/events/jbd2.h
kernel/debug/gdbstub.c
kernel/debug/kdb/kdb_bt.c
kernel/debug/kdb/kdb_cmds
kernel/debug/kdb/kdb_debugger.c
kernel/debug/kdb/kdb_io.c
kernel/debug/kdb/kdb_main.c
kernel/debug/kdb/kdb_private.h
mm/oom_kill.c
sound/core/pcm_compat.c
sound/core/rtctimer.c
sound/pci/asihpi/hpidspcd.c
sound/pci/asihpi/hpioctl.c
sound/pci/rme9652/hdspm.c
sound/soc/txx9/txx9aclc.c

index ddf451ee2a08812eb16a28f3264618ca2e1e749b..ff1df4e3b05962741195df437d8ec2891f3e29af 100644 (file)
@@ -39,3 +39,9 @@ Description:  Generic interface to platform dependent persistent storage.
                multiple) files based on the record size of the underlying
                persistent storage until at least this amount is reached.
                Default is 10 Kbytes.
+
+               Pstore only supports one backend at a time. If multiple
+               backends are available, the preferred backend may be
+               set by passing the pstore.backend= argument to the kernel at
+               boot time.
+
index 6b5c42dbbe841c8ec6ed1b608ac3bd77ffe60fa1..2c656ae43ba7f9907571c181ad4800ea5ceeaa9f 100644 (file)
@@ -4,7 +4,8 @@ dm-crypt
 Device-Mapper's "crypt" target provides transparent encryption of block devices
 using the kernel crypto API.
 
-Parameters: <cipher> <key> <iv_offset> <device path> <offset>
+Parameters: <cipher> <key> <iv_offset> <device path> \
+             <offset> [<#opt_params> <opt_params>]
 
 <cipher>
     Encryption cipher and an optional IV generation mode.
@@ -37,6 +38,24 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
 <offset>
     Starting sector within the device where the encrypted data begins.
 
+<#opt_params>
+    Number of optional parameters. If there are no optional parameters,
+    the optional paramaters section can be skipped or #opt_params can be zero.
+    Otherwise #opt_params is the number of following arguments.
+
+    Example of optional parameters section:
+        1 allow_discards
+
+allow_discards
+    Block discard requests (a.k.a. TRIM) are passed through the crypt device.
+    The default is to ignore discard requests.
+
+    WARNING: Assess the specific security risks carefully before enabling this
+    option.  For example, allowing discards on encrypted devices may lead to
+    the leak of information about the ciphertext device (filesystem type,
+    used space etc.) if the discarded blocks can be located easily on the
+    device later.
+
 Example scripts
 ===============
 LUKS (Linux Unified Key Setup) is now the preferred way to set up disk
index c8efdfd19a655d02bd14f67d41abf45e16d1e9fd..6ff5c2327227f2040e43ec2b74f97a2bd74ca11f 100644 (file)
@@ -1,17 +1,53 @@
 dm-flakey
 =========
 
-This target is the same as the linear target except that it returns I/O
-errors periodically.  It's been found useful in simulating failing
-devices for testing purposes.
+This target is the same as the linear target except that it exhibits
+unreliable behaviour periodically.  It's been found useful in simulating
+failing devices for testing purposes.
 
 Starting from the time the table is loaded, the device is available for
-<up interval> seconds, then returns errors for <down interval> seconds,
-and then this cycle repeats.
+<up interval> seconds, then exhibits unreliable behaviour for <down
+interval> seconds, and then this cycle repeats.
 
-Parameters: <dev path> <offset> <up interval> <down interval>
+Also, consider using this in combination with the dm-delay target too,
+which can delay reads and writes and/or send them to different
+underlying devices.
+
+Table parameters
+----------------
+  <dev path> <offset> <up interval> <down interval> \
+    [<num_features> [<feature arguments>]]
+
+Mandatory parameters:
     <dev path>: Full pathname to the underlying block-device, or a
                 "major:minor" device-number.
     <offset>: Starting sector within the device.
     <up interval>: Number of seconds device is available.
     <down interval>: Number of seconds device returns errors.
+
+Optional feature parameters:
+  If no feature parameters are present, during the periods of
+  unreliability, all I/O returns errors.
+
+  drop_writes:
+       All write I/O is silently ignored.
+       Read I/O is handled correctly.
+
+  corrupt_bio_byte <Nth_byte> <direction> <value> <flags>:
+       During <down interval>, replace <Nth_byte> of the data of
+       each matching bio with <value>.
+
+    <Nth_byte>: The offset of the byte to replace.
+               Counting starts at 1, to replace the first byte.
+    <direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
+                'w' is incompatible with drop_writes.
+    <value>: The value (from 0-255) to write.
+    <flags>: Perform the replacement only if bio->bi_rw has all the
+            selected flags set.
+
+Examples:
+  corrupt_bio_byte 32 r 1 0
+       - replaces the 32nd byte of READ bios with the value 1
+
+  corrupt_bio_byte 224 w 0 32
+       - replaces the 224th byte of REQ_META (=32) bios with the value 0
index 33b6b7071ac8d65e386aead1e4e6f6a916e9c502..2a8c11331d2d6e8a861d272546e8a7f7d9f02cf8 100644 (file)
-Device-mapper RAID (dm-raid) is a bridge from DM to MD.  It
-provides a way to use device-mapper interfaces to access the MD RAID
-drivers.
+dm-raid
+-------
 
-As with all device-mapper targets, the nominal public interfaces are the
-constructor (CTR) tables and the status outputs (both STATUSTYPE_INFO
-and STATUSTYPE_TABLE).  The CTR table looks like the following:
+The device-mapper RAID (dm-raid) target provides a bridge from DM to MD.
+It allows the MD RAID drivers to be accessed using a device-mapper
+interface.
 
-1: <s> <l> raid \
-2:      <raid_type> <#raid_params> <raid_params> \
-3:      <#raid_devs> <meta_dev1> <dev1> .. <meta_devN> <devN>
-
-Line 1 contains the standard first three arguments to any device-mapper
-target - the start, length, and target type fields.  The target type in
-this case is "raid".
-
-Line 2 contains the arguments that define the particular raid
-type/personality/level, the required arguments for that raid type, and
-any optional arguments.  Possible raid types include: raid4, raid5_la,
-raid5_ls, raid5_rs, raid6_zr, raid6_nr, and raid6_nc.  (raid1 is
-planned for the future.)  The list of required and optional parameters
-is the same for all the current raid types.  The required parameters are
-positional, while the optional parameters are given as key/value pairs.
-The possible parameters are as follows:
- <chunk_size>           Chunk size in sectors.
- [[no]sync]             Force/Prevent RAID initialization
- [rebuild <idx>]        Rebuild the drive indicated by the index
- [daemon_sleep <ms>]    Time between bitmap daemon work to clear bits
- [min_recovery_rate <kB/sec/disk>]      Throttle RAID initialization
- [max_recovery_rate <kB/sec/disk>]      Throttle RAID initialization
- [max_write_behind <sectors>]           See '-write-behind=' (man mdadm)
- [stripe_cache <sectors>]               Stripe cache size for higher RAIDs
-
-Line 3 contains the list of devices that compose the array in
-metadata/data device pairs.  If the metadata is stored separately, a '-'
-is given for the metadata device position.  If a drive has failed or is
-missing at creation time, a '-' can be given for both the metadata and
-data drives for a given position.
-
-NB. Currently all metadata devices must be specified as '-'.
-
-Examples:
-# RAID4 - 4 data drives, 1 parity
+The target is named "raid" and it accepts the following parameters:
+
+  <raid_type> <#raid_params> <raid_params> \
+    <#raid_devs> <metadata_dev0> <dev0> [.. <metadata_devN> <devN>]
+
+<raid_type>:
+  raid1                RAID1 mirroring
+  raid4                RAID4 dedicated parity disk
+  raid5_la     RAID5 left asymmetric
+               - rotating parity 0 with data continuation
+  raid5_ra     RAID5 right asymmetric
+               - rotating parity N with data continuation
+  raid5_ls     RAID5 left symmetric
+               - rotating parity 0 with data restart
+  raid5_rs     RAID5 right symmetric
+               - rotating parity N with data restart
+  raid6_zr     RAID6 zero restart
+               - rotating parity zero (left-to-right) with data restart
+  raid6_nr     RAID6 N restart
+               - rotating parity N (right-to-left) with data restart
+  raid6_nc     RAID6 N continue
+               - rotating parity N (right-to-left) with data continuation
+
+  Refererence: Chapter 4 of
+  http://www.snia.org/sites/default/files/SNIA_DDF_Technical_Position_v2.0.pdf
+
+<#raid_params>: The number of parameters that follow.
+
+<raid_params> consists of
+    Mandatory parameters:
+        <chunk_size>: Chunk size in sectors.  This parameter is often known as
+                     "stripe size".  It is the only mandatory parameter and
+                     is placed first.
+
+    followed by optional parameters (in any order):
+       [sync|nosync]   Force or prevent RAID initialization.
+
+       [rebuild <idx>] Rebuild drive number idx (first drive is 0).
+
+       [daemon_sleep <ms>]
+               Interval between runs of the bitmap daemon that
+               clear bits.  A longer interval means less bitmap I/O but
+               resyncing after a failure is likely to take longer.
+
+       [min_recovery_rate <kB/sec/disk>]  Throttle RAID initialization
+       [max_recovery_rate <kB/sec/disk>]  Throttle RAID initialization
+       [write_mostly <idx>]               Drive index is write-mostly
+       [max_write_behind <sectors>]       See '-write-behind=' (man mdadm)
+       [stripe_cache <sectors>]           Stripe cache size (higher RAIDs only)
+       [region_size <sectors>]
+               The region_size multiplied by the number of regions is the
+               logical size of the array.  The bitmap records the device
+               synchronisation state for each region.
+
+<#raid_devs>: The number of devices composing the array.
+       Each device consists of two entries.  The first is the device
+       containing the metadata (if any); the second is the one containing the
+       data.
+
+       If a drive has failed or is missing at creation time, a '-' can be
+       given for both the metadata and data drives for a given position.
+
+
+Example tables
+--------------
+# RAID4 - 4 data drives, 1 parity (no metadata devices)
 # No metadata devices specified to hold superblock/bitmap info
 # Chunk size of 1MiB
 # (Lines separated for easy reading)
+
 0 1960893648 raid \
         raid4 1 2048 \
         5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
 
-# RAID4 - 4 data drives, 1 parity (no metadata devices)
+# RAID4 - 4 data drives, 1 parity (with metadata devices)
 # Chunk size of 1MiB, force RAID initialization,
 #       min recovery rate at 20 kiB/sec/disk
+
 0 1960893648 raid \
-        raid4 4 2048 min_recovery_rate 20 sync\
-        5 - 8:17 - 8:33 - 8:49 - 8:65 - 8:81
+        raid4 4 2048 sync min_recovery_rate 20 \
+        5 8:17 8:18 8:33 8:34 8:49 8:50 8:65 8:66 8:81 8:82
 
-Performing a 'dmsetup table' should display the CTR table used to
-construct the mapping (with possible reordering of optional
-parameters).
+'dmsetup table' displays the table used to construct the mapping.
+The optional parameters are always printed in the order listed
+above with "sync" or "nosync" always output ahead of the other
+arguments, regardless of the order used when originally loading the table.
+Arguments that can be repeated are ordered by value.
 
-Performing a 'dmsetup status' will yield information on the state and
-health of the array.  The output is as follows:
+'dmsetup status' yields information on the state and health of the
+array.
+The output is as follows:
 1: <s> <l> raid \
 2:      <raid_type> <#devices> <1 health char for each dev> <resync_ratio>
 
-Line 1 is standard DM output.  Line 2 is best shown by example:
+Line 1 is the standard output produced by device-mapper.
+Line 2 is produced by the raid target, and best explained by example:
         0 1960893648 raid raid4 5 AAAAA 2/490221568
 Here we can see the RAID type is raid4, there are 5 devices - all of
 which are 'A'live, and the array is 2/490221568 complete with recovery.
+Faulty or missing devices are marked 'D'.  Devices that are out-of-sync
+are marked 'a'.
index 5a0cb1ef6164d5c363f6f4b8de0007e7820ea5ef..94b7e0f96b38fa8086ea14f7cd88718b45e84d70 100644 (file)
@@ -10,87 +10,181 @@ NOTE: For DMA Engine usage in async_tx please see:
 Below is a guide to device driver writers on how to use the Slave-DMA API of the
 DMA Engine. This is applicable only for slave DMA usage only.
 
-The slave DMA usage consists of following steps
+The slave DMA usage consists of following steps:
 1. Allocate a DMA slave channel
 2. Set slave and controller specific parameters
 3. Get a descriptor for transaction
-4. Submit the transaction and wait for callback notification
+4. Submit the transaction
+5. Issue pending requests and wait for callback notification
 
 1. Allocate a DMA slave channel
-Channel allocation is slightly different in the slave DMA context, client
-drivers typically need a channel from a particular DMA controller only and even
-in some cases a specific channel is desired. To request a channel
-dma_request_channel() API is used.
-
-Interface:
-struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
-               dma_filter_fn filter_fn,
-               void *filter_param);
-where dma_filter_fn is defined as:
-typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
-
-When the optional 'filter_fn' parameter is set to NULL dma_request_channel
-simply returns the first channel that satisfies the capability mask.  Otherwise,
-when the mask parameter is insufficient for specifying the necessary channel,
-the filter_fn routine can be used to disposition the available channels in the
-system. The filter_fn routine is called once for each free channel in the
-system.  Upon seeing a suitable channel filter_fn returns DMA_ACK which flags
-that channel to be the return value from dma_request_channel.  A channel
-allocated via this interface is exclusive to the caller, until
-dma_release_channel() is called.
+
+   Channel allocation is slightly different in the slave DMA context,
+   client drivers typically need a channel from a particular DMA
+   controller only and even in some cases a specific channel is desired.
+   To request a channel dma_request_channel() API is used.
+
+   Interface:
+       struct dma_chan *dma_request_channel(dma_cap_mask_t mask,
+                       dma_filter_fn filter_fn,
+                       void *filter_param);
+   where dma_filter_fn is defined as:
+       typedef bool (*dma_filter_fn)(struct dma_chan *chan, void *filter_param);
+
+   The 'filter_fn' parameter is optional, but highly recommended for
+   slave and cyclic channels as they typically need to obtain a specific
+   DMA channel.
+
+   When the optional 'filter_fn' parameter is NULL, dma_request_channel()
+   simply returns the first channel that satisfies the capability mask.
+
+   Otherwise, the 'filter_fn' routine will be called once for each free
+   channel which has a capability in 'mask'.  'filter_fn' is expected to
+   return 'true' when the desired DMA channel is found.
+
+   A channel allocated via this interface is exclusive to the caller,
+   until dma_release_channel() is called.
 
 2. Set slave and controller specific parameters
-Next step is always to pass some specific information to the DMA driver. Most of
-the generic information which a slave DMA can use is in struct dma_slave_config.
-It allows the clients to specify DMA direction, DMA addresses, bus widths, DMA
-burst lengths etc. If some DMA controllers have more parameters to be sent then
-they should try to embed struct dma_slave_config in their controller specific
-structure. That gives flexibility to client to pass more parameters, if
-required.
-
-Interface:
-int dmaengine_slave_config(struct dma_chan *chan,
-                                         struct dma_slave_config *config)
+
+   Next step is always to pass some specific information to the DMA
+   driver.  Most of the generic information which a slave DMA can use
+   is in struct dma_slave_config.  This allows the clients to specify
+   DMA direction, DMA addresses, bus widths, DMA burst lengths etc
+   for the peripheral.
+
+   If some DMA controllers have more parameters to be sent then they
+   should try to embed struct dma_slave_config in their controller
+   specific structure. That gives flexibility to client to pass more
+   parameters, if required.
+
+   Interface:
+       int dmaengine_slave_config(struct dma_chan *chan,
+                                 struct dma_slave_config *config)
+
+   Please see the dma_slave_config structure definition in dmaengine.h
+   for a detailed explaination of the struct members.  Please note
+   that the 'direction' member will be going away as it duplicates the
+   direction given in the prepare call.
 
 3. Get a descriptor for transaction
-For slave usage the various modes of slave transfers supported by the
-DMA-engine are:
-slave_sg       - DMA a list of scatter gather buffers from/to a peripheral
-dma_cyclic     - Perform a cyclic DMA operation from/to a peripheral till the
+
+   For slave usage the various modes of slave transfers supported by the
+   DMA-engine are:
+
+   slave_sg    - DMA a list of scatter gather buffers from/to a peripheral
+   dma_cyclic  - Perform a cyclic DMA operation from/to a peripheral till the
                  operation is explicitly stopped.
-The non NULL return of this transfer API represents a "descriptor" for the given
-transaction.
-
-Interface:
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_sg)(
-               struct dma_chan *chan,
-               struct scatterlist *dst_sg, unsigned int dst_nents,
-               struct scatterlist *src_sg, unsigned int src_nents,
+
+   A non-NULL return of this transfer API represents a "descriptor" for
+   the given transaction.
+
+   Interface:
+       struct dma_async_tx_descriptor *(*chan->device->device_prep_slave_sg)(
+               struct dma_chan *chan, struct scatterlist *sgl,
+               unsigned int sg_len, enum dma_data_direction direction,
                unsigned long flags);
-struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
+
+       struct dma_async_tx_descriptor *(*chan->device->device_prep_dma_cyclic)(
                struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
                size_t period_len, enum dma_data_direction direction);
 
-4. Submit the transaction and wait for callback notification
-To schedule the transaction to be scheduled by dma device, the "descriptor"
-returned in above (3) needs to be submitted.
-To tell the dma driver that a transaction is ready to be serviced, the
-descriptor->submit() callback needs to be invoked. This chains the descriptor to
-the pending queue.
-The transactions in the pending queue can be activated by calling the
-issue_pending API. If channel is idle then the first transaction in queue is
-started and subsequent ones queued up.
-On completion of the DMA operation the next in queue is submitted and a tasklet
-triggered. The tasklet would then call the client driver completion callback
-routine for notification, if set.
-Interface:
-void dma_async_issue_pending(struct dma_chan *chan);
-
-==============================================================================
-
-Additional usage notes for dma driver writers
-1/ Although DMA engine specifies that completion callback routines cannot submit
-any new operations, but typically for slave DMA subsequent transaction may not
-be available for submit prior to callback routine being called. This requirement
-is not a requirement for DMA-slave devices. But they should take care to drop
-the spin-lock they might be holding before calling the callback routine
+   The peripheral driver is expected to have mapped the scatterlist for
+   the DMA operation prior to calling device_prep_slave_sg, and must
+   keep the scatterlist mapped until the DMA operation has completed.
+   The scatterlist must be mapped using the DMA struct device.  So,
+   normal setup should look like this:
+
+       nr_sg = dma_map_sg(chan->device->dev, sgl, sg_len);
+       if (nr_sg == 0)
+               /* error */
+
+       desc = chan->device->device_prep_slave_sg(chan, sgl, nr_sg,
+                       direction, flags);
+
+   Once a descriptor has been obtained, the callback information can be
+   added and the descriptor must then be submitted.  Some DMA engine
+   drivers may hold a spinlock between a successful preparation and
+   submission so it is important that these two operations are closely
+   paired.
+
+   Note:
+       Although the async_tx API specifies that completion callback
+       routines cannot submit any new operations, this is not the
+       case for slave/cyclic DMA.
+
+       For slave DMA, the subsequent transaction may not be available
+       for submission prior to callback function being invoked, so
+       slave DMA callbacks are permitted to prepare and submit a new
+       transaction.
+
+       For cyclic DMA, a callback function may wish to terminate the
+       DMA via dmaengine_terminate_all().
+
+       Therefore, it is important that DMA engine drivers drop any
+       locks before calling the callback function which may cause a
+       deadlock.
+
+       Note that callbacks will always be invoked from the DMA
+       engines tasklet, never from interrupt context.
+
+4. Submit the transaction
+
+   Once the descriptor has been prepared and the callback information
+   added, it must be placed on the DMA engine drivers pending queue.
+
+   Interface:
+       dma_cookie_t dmaengine_submit(struct dma_async_tx_descriptor *desc)
+
+   This returns a cookie can be used to check the progress of DMA engine
+   activity via other DMA engine calls not covered in this document.
+
+   dmaengine_submit() will not start the DMA operation, it merely adds
+   it to the pending queue.  For this, see step 5, dma_async_issue_pending.
+
+5. Issue pending DMA requests and wait for callback notification
+
+   The transactions in the pending queue can be activated by calling the
+   issue_pending API. If channel is idle then the first transaction in
+   queue is started and subsequent ones queued up.
+
+   On completion of each DMA operation, the next in queue is started and
+   a tasklet triggered. The tasklet will then call the client driver
+   completion callback routine for notification, if set.
+
+   Interface:
+       void dma_async_issue_pending(struct dma_chan *chan);
+
+Further APIs:
+
+1. int dmaengine_terminate_all(struct dma_chan *chan)
+
+   This causes all activity for the DMA channel to be stopped, and may
+   discard data in the DMA FIFO which hasn't been fully transferred.
+   No callback functions will be called for any incomplete transfers.
+
+2. int dmaengine_pause(struct dma_chan *chan)
+
+   This pauses activity on the DMA channel without data loss.
+
+3. int dmaengine_resume(struct dma_chan *chan)
+
+   Resume a previously paused DMA channel.  It is invalid to resume a
+   channel which is not currently paused.
+
+4. enum dma_status dma_async_is_tx_complete(struct dma_chan *chan,
+        dma_cookie_t cookie, dma_cookie_t *last, dma_cookie_t *used)
+
+   This can be used to check the status of the channel.  Please see
+   the documentation in include/linux/dmaengine.h for a more complete
+   description of this API.
+
+   This can be used in conjunction with dma_async_is_complete() and
+   the cookie returned from 'descriptor->submit()' to check for
+   completion of a specific DMA transaction.
+
+   Note:
+       Not all DMA engine drivers can return reliable information for
+       a running DMA channel.  It is recommended that DMA engine users
+       pause or stop (via dmaengine_terminate_all) the channel before
+       using this API.
index 4ca93898fbd3d984a3bccf550b5281d3f0a90e56..26a83743af19d3e3f46e309dd28f5af203f8b1c4 100644 (file)
@@ -2153,6 +2153,8 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        [HW,MOUSE] Controls Logitech smartscroll autorepeat.
                        0 = disabled, 1 = enabled (default).
 
+       pstore.backend= Specify the name of the pstore backend to use
+
        pt.             [PARIDE]
                        See Documentation/blockdev/paride.txt.
 
index 137b277f7e56b073a4ef2cbc4fef4429e28053b1..64c7ab7e7a816922b8830bee7b4e579f1c48d2c4 100644 (file)
@@ -27,6 +27,7 @@ config IA64
        select GENERIC_PENDING_IRQ if SMP
        select IRQ_PER_CPU
        select GENERIC_IRQ_SHOW
+       select ARCH_WANT_OPTIONAL_GPIOLIB
        default y
        help
          The Itanium Processor Family is Intel's 64-bit successor to
@@ -89,6 +90,9 @@ config GENERIC_TIME_VSYSCALL
 config HAVE_SETUP_PER_CPU_AREA
        def_bool y
 
+config GENERIC_GPIO
+       def_bool y
+
 config DMI
        bool
        default y
diff --git a/arch/ia64/include/asm/gpio.h b/arch/ia64/include/asm/gpio.h
new file mode 100644 (file)
index 0000000..590a20d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Generic GPIO API implementation for IA-64.
+ *
+ * A stright copy of that for PowerPC which was:
+ *
+ * Copyright (c) 2007-2008  MontaVista Software, Inc.
+ *
+ * Author: Anton Vorontsov <avorontsov@ru.mvista.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#ifndef _ASM_IA64_GPIO_H
+#define _ASM_IA64_GPIO_H
+
+#include <linux/errno.h>
+#include <asm-generic/gpio.h>
+
+#ifdef CONFIG_GPIOLIB
+
+/*
+ * We don't (yet) implement inlined/rapid versions for on-chip gpios.
+ * Just call gpiolib.
+ */
+static inline int gpio_get_value(unsigned int gpio)
+{
+       return __gpio_get_value(gpio);
+}
+
+static inline void gpio_set_value(unsigned int gpio, int value)
+{
+       __gpio_set_value(gpio, value);
+}
+
+static inline int gpio_cansleep(unsigned int gpio)
+{
+       return __gpio_cansleep(gpio);
+}
+
+static inline int gpio_to_irq(unsigned int gpio)
+{
+       return __gpio_to_irq(gpio);
+}
+
+static inline int irq_to_gpio(unsigned int irq)
+{
+       return -EINVAL;
+}
+
+#endif /* CONFIG_GPIOLIB */
+
+#endif /* _ASM_IA64_GPIO_H */
index e6cef8e1b53455db9927668c1da1157c364e6b6e..6053f4780df94b67aa2f9bffc535e72c2c03ab94 100644 (file)
@@ -932,8 +932,11 @@ static int erst_check_table(struct acpi_table_erst *erst_tab)
 static int erst_open_pstore(struct pstore_info *psi);
 static int erst_close_pstore(struct pstore_info *psi);
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
-                      struct timespec *time);
-static u64 erst_writer(enum pstore_type_id type, size_t size);
+                          struct timespec *time, struct pstore_info *psi);
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+                      size_t size, struct pstore_info *psi);
+static int erst_clearer(enum pstore_type_id type, u64 id,
+                       struct pstore_info *psi);
 
 static struct pstore_info erst_info = {
        .owner          = THIS_MODULE,
@@ -942,7 +945,7 @@ static struct pstore_info erst_info = {
        .close          = erst_close_pstore,
        .read           = erst_reader,
        .write          = erst_writer,
-       .erase          = erst_clear
+       .erase          = erst_clearer
 };
 
 #define CPER_CREATOR_PSTORE                                            \
@@ -983,7 +986,7 @@ static int erst_close_pstore(struct pstore_info *psi)
 }
 
 static ssize_t erst_reader(u64 *id, enum pstore_type_id *type,
-                      struct timespec *time)
+                          struct timespec *time, struct pstore_info *psi)
 {
        int rc;
        ssize_t len = 0;
@@ -1037,7 +1040,8 @@ out:
        return (rc < 0) ? rc : (len - sizeof(*rcd));
 }
 
-static u64 erst_writer(enum pstore_type_id type, size_t size)
+static u64 erst_writer(enum pstore_type_id type, unsigned int part,
+                      size_t size, struct pstore_info *psi)
 {
        struct cper_pstore_record *rcd = (struct cper_pstore_record *)
                                        (erst_info.buf - sizeof(*rcd));
@@ -1080,6 +1084,12 @@ static u64 erst_writer(enum pstore_type_id type, size_t size)
        return rcd->hdr.record_id;
 }
 
+static int erst_clearer(enum pstore_type_id type, u64 id,
+                       struct pstore_info *psi)
+{
+       return erst_clear(id);
+}
+
 static int __init erst_init(void)
 {
        int rc = 0;
index b89fffc1d777b277abd7bac4221b0fbc98e265ff..33e1bed68fddf771ae9b12a716114215b59b5f68 100644 (file)
@@ -166,7 +166,7 @@ static int create_path(const char *nodepath)
 {
        char *path;
        char *s;
-       int err;
+       int err = 0;
 
        /* parent directories do not exist, create them */
        path = kstrdup(nodepath, GFP_KERNEL);
index a4af8589330cceddfc613ef381d45b2a3a040540..734ed0206cd5ead8f9cc554e94ff168b59fc3dae 100644 (file)
@@ -9,6 +9,5 @@ TODO for slave dma
        - mxs-dma.c
        - dw_dmac
        - intel_mid_dma
-       - ste_dma40
 4. Check other subsystems for dma drivers and merge/move to dmaengine
 5. Remove dma_slave_config's dma direction.
index e6d7228b1479a6d462b1e19c323541fe5a767a7f..196a7378d33238ec0a08c1ac3672d428fff370cd 100644 (file)
@@ -156,14 +156,10 @@ struct pl08x_driver_data {
 #define PL08X_BOUNDARY_SHIFT           (10)    /* 1KB 0x400 */
 #define PL08X_BOUNDARY_SIZE            (1 << PL08X_BOUNDARY_SHIFT)
 
-/* Minimum period between work queue runs */
-#define PL08X_WQ_PERIODMIN     20
-
 /* Size (bytes) of each LLI buffer allocated for one transfer */
 # define PL08X_LLI_TSFR_SIZE   0x2000
 
 /* Maximum times we call dma_pool_alloc on this pool without freeing */
-#define PL08X_MAX_ALLOCS       0x40
 #define MAX_NUM_TSFR_LLIS      (PL08X_LLI_TSFR_SIZE/sizeof(struct pl08x_lli))
 #define PL08X_ALIGN            8
 
@@ -495,10 +491,10 @@ static inline u32 pl08x_cctl_bits(u32 cctl, u8 srcwidth, u8 dstwidth,
 
 struct pl08x_lli_build_data {
        struct pl08x_txd *txd;
-       struct pl08x_driver_data *pl08x;
        struct pl08x_bus_data srcbus;
        struct pl08x_bus_data dstbus;
        size_t remainder;
+       u32 lli_bus;
 };
 
 /*
@@ -551,8 +547,7 @@ static void pl08x_fill_lli_for_desc(struct pl08x_lli_build_data *bd,
        llis_va[num_llis].src = bd->srcbus.addr;
        llis_va[num_llis].dst = bd->dstbus.addr;
        llis_va[num_llis].lli = llis_bus + (num_llis + 1) * sizeof(struct pl08x_lli);
-       if (bd->pl08x->lli_buses & PL08X_AHB2)
-               llis_va[num_llis].lli |= PL080_LLI_LM_AHB2;
+       llis_va[num_llis].lli |= bd->lli_bus;
 
        if (cctl & PL080_CONTROL_SRC_INCR)
                bd->srcbus.addr += len;
@@ -605,9 +600,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        cctl = txd->cctl;
 
        bd.txd = txd;
-       bd.pl08x = pl08x;
        bd.srcbus.addr = txd->src_addr;
        bd.dstbus.addr = txd->dst_addr;
+       bd.lli_bus = (pl08x->lli_buses & PL08X_AHB2) ? PL080_LLI_LM_AHB2 : 0;
 
        /* Find maximum width of the source bus */
        bd.srcbus.maxwidth =
@@ -622,25 +617,15 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        /* Set up the bus widths to the maximum */
        bd.srcbus.buswidth = bd.srcbus.maxwidth;
        bd.dstbus.buswidth = bd.dstbus.maxwidth;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s source bus is %d bytes wide, dest bus is %d bytes wide\n",
-                __func__, bd.srcbus.buswidth, bd.dstbus.buswidth);
-
 
        /*
         * Bytes transferred == tsize * MIN(buswidths), not max(buswidths)
         */
        max_bytes_per_lli = min(bd.srcbus.buswidth, bd.dstbus.buswidth) *
                PL080_CONTROL_TRANSFER_SIZE_MASK;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s max bytes per lli = %zu\n",
-                __func__, max_bytes_per_lli);
 
        /* We need to count this down to zero */
        bd.remainder = txd->len;
-       dev_vdbg(&pl08x->adev->dev,
-                "%s remainder = %zu\n",
-                __func__, bd.remainder);
 
        /*
         * Choose bus to align to
@@ -649,6 +634,16 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
         */
        pl08x_choose_master_bus(&bd, &mbus, &sbus, cctl);
 
+       dev_vdbg(&pl08x->adev->dev, "src=0x%08x%s/%u dst=0x%08x%s/%u len=%zu llimax=%zu\n",
+                bd.srcbus.addr, cctl & PL080_CONTROL_SRC_INCR ? "+" : "",
+                bd.srcbus.buswidth,
+                bd.dstbus.addr, cctl & PL080_CONTROL_DST_INCR ? "+" : "",
+                bd.dstbus.buswidth,
+                bd.remainder, max_bytes_per_lli);
+       dev_vdbg(&pl08x->adev->dev, "mbus=%s sbus=%s\n",
+                mbus == &bd.srcbus ? "src" : "dst",
+                sbus == &bd.srcbus ? "src" : "dst");
+
        if (txd->len < mbus->buswidth) {
                /* Less than a bus width available - send as single bytes */
                while (bd.remainder) {
@@ -840,15 +835,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
        {
                int i;
 
+               dev_vdbg(&pl08x->adev->dev,
+                        "%-3s %-9s  %-10s %-10s %-10s %s\n",
+                        "lli", "", "csrc", "cdst", "clli", "cctl");
                for (i = 0; i < num_llis; i++) {
                        dev_vdbg(&pl08x->adev->dev,
-                                "lli %d @%p: csrc=0x%08x, cdst=0x%08x, cctl=0x%08x, clli=0x%08x\n",
-                                i,
-                                &llis_va[i],
-                                llis_va[i].src,
-                                llis_va[i].dst,
-                                llis_va[i].cctl,
-                                llis_va[i].lli
+                                "%3d @%p: 0x%08x 0x%08x 0x%08x 0x%08x\n",
+                                i, &llis_va[i], llis_va[i].src,
+                                llis_va[i].dst, llis_va[i].lli, llis_va[i].cctl
                                );
                }
        }
@@ -1054,64 +1048,105 @@ pl08x_dma_tx_status(struct dma_chan *chan,
 
 /* PrimeCell DMA extension */
 struct burst_table {
-       int burstwords;
+       u32 burstwords;
        u32 reg;
 };
 
 static const struct burst_table burst_sizes[] = {
        {
                .burstwords = 256,
-               .reg = (PL080_BSIZE_256 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_256 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_256,
        },
        {
                .burstwords = 128,
-               .reg = (PL080_BSIZE_128 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_128 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_128,
        },
        {
                .burstwords = 64,
-               .reg = (PL080_BSIZE_64 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_64 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_64,
        },
        {
                .burstwords = 32,
-               .reg = (PL080_BSIZE_32 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_32 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_32,
        },
        {
                .burstwords = 16,
-               .reg = (PL080_BSIZE_16 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_16 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_16,
        },
        {
                .burstwords = 8,
-               .reg = (PL080_BSIZE_8 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_8 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_8,
        },
        {
                .burstwords = 4,
-               .reg = (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .reg = PL080_BSIZE_4,
        },
        {
-               .burstwords = 1,
-               .reg = (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT),
+               .burstwords = 0,
+               .reg = PL080_BSIZE_1,
        },
 };
 
+/*
+ * Given the source and destination available bus masks, select which
+ * will be routed to each port.  We try to have source and destination
+ * on separate ports, but always respect the allowable settings.
+ */
+static u32 pl08x_select_bus(u8 src, u8 dst)
+{
+       u32 cctl = 0;
+
+       if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
+               cctl |= PL080_CONTROL_DST_AHB2;
+       if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
+               cctl |= PL080_CONTROL_SRC_AHB2;
+
+       return cctl;
+}
+
+static u32 pl08x_cctl(u32 cctl)
+{
+       cctl &= ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
+                 PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
+                 PL080_CONTROL_PROT_MASK);
+
+       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
+       return cctl | PL080_CONTROL_PROT_SYS;
+}
+
+static u32 pl08x_width(enum dma_slave_buswidth width)
+{
+       switch (width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               return PL080_WIDTH_8BIT;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               return PL080_WIDTH_16BIT;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               return PL080_WIDTH_32BIT;
+       default:
+               return ~0;
+       }
+}
+
+static u32 pl08x_burst(u32 maxburst)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
+               if (burst_sizes[i].burstwords <= maxburst)
+                       break;
+
+       return burst_sizes[i].reg;
+}
+
 static int dma_set_runtime_config(struct dma_chan *chan,
                                  struct dma_slave_config *config)
 {
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        struct pl08x_driver_data *pl08x = plchan->host;
-       struct pl08x_channel_data *cd = plchan->cd;
        enum dma_slave_buswidth addr_width;
-       dma_addr_t addr;
-       u32 maxburst;
+       u32 width, burst, maxburst;
        u32 cctl = 0;
-       int i;
 
        if (!plchan->slave)
                return -EINVAL;
@@ -1119,11 +1154,9 @@ static int dma_set_runtime_config(struct dma_chan *chan,
        /* Transfer direction */
        plchan->runtime_direction = config->direction;
        if (config->direction == DMA_TO_DEVICE) {
-               addr = config->dst_addr;
                addr_width = config->dst_addr_width;
                maxburst = config->dst_maxburst;
        } else if (config->direction == DMA_FROM_DEVICE) {
-               addr = config->src_addr;
                addr_width = config->src_addr_width;
                maxburst = config->src_maxburst;
        } else {
@@ -1132,46 +1165,40 @@ static int dma_set_runtime_config(struct dma_chan *chan,
                return -EINVAL;
        }
 
-       switch (addr_width) {
-       case DMA_SLAVE_BUSWIDTH_1_BYTE:
-               cctl |= (PL080_WIDTH_8BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_8BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       case DMA_SLAVE_BUSWIDTH_2_BYTES:
-               cctl |= (PL080_WIDTH_16BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_16BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       case DMA_SLAVE_BUSWIDTH_4_BYTES:
-               cctl |= (PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT) |
-                       (PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT);
-               break;
-       default:
+       width = pl08x_width(addr_width);
+       if (width == ~0) {
                dev_err(&pl08x->adev->dev,
                        "bad runtime_config: alien address width\n");
                return -EINVAL;
        }
 
+       cctl |= width << PL080_CONTROL_SWIDTH_SHIFT;
+       cctl |= width << PL080_CONTROL_DWIDTH_SHIFT;
+
        /*
-        * Now decide on a maxburst:
         * If this channel will only request single transfers, set this
         * down to ONE element.  Also select one element if no maxburst
         * is specified.
         */
-       if (plchan->cd->single || maxburst == 0) {
-               cctl |= (PL080_BSIZE_1 << PL080_CONTROL_SB_SIZE_SHIFT) |
-                       (PL080_BSIZE_1 << PL080_CONTROL_DB_SIZE_SHIFT);
+       if (plchan->cd->single)
+               maxburst = 1;
+
+       burst = pl08x_burst(maxburst);
+       cctl |= burst << PL080_CONTROL_SB_SIZE_SHIFT;
+       cctl |= burst << PL080_CONTROL_DB_SIZE_SHIFT;
+
+       if (plchan->runtime_direction == DMA_FROM_DEVICE) {
+               plchan->src_addr = config->src_addr;
+               plchan->src_cctl = pl08x_cctl(cctl) | PL080_CONTROL_DST_INCR |
+                       pl08x_select_bus(plchan->cd->periph_buses,
+                                        pl08x->mem_buses);
        } else {
-               for (i = 0; i < ARRAY_SIZE(burst_sizes); i++)
-                       if (burst_sizes[i].burstwords <= maxburst)
-                               break;
-               cctl |= burst_sizes[i].reg;
+               plchan->dst_addr = config->dst_addr;
+               plchan->dst_cctl = pl08x_cctl(cctl) | PL080_CONTROL_SRC_INCR |
+                       pl08x_select_bus(pl08x->mem_buses,
+                                        plchan->cd->periph_buses);
        }
 
-       plchan->runtime_addr = addr;
-
-       /* Modify the default channel data to fit PrimeCell request */
-       cd->cctl = cctl;
-
        dev_dbg(&pl08x->adev->dev,
                "configured channel %s (%s) for %s, data width %d, "
                "maxburst %d words, LE, CCTL=0x%08x\n",
@@ -1270,23 +1297,6 @@ static int pl08x_prep_channel_resources(struct pl08x_dma_chan *plchan,
        return 0;
 }
 
-/*
- * Given the source and destination available bus masks, select which
- * will be routed to each port.  We try to have source and destination
- * on separate ports, but always respect the allowable settings.
- */
-static u32 pl08x_select_bus(struct pl08x_driver_data *pl08x, u8 src, u8 dst)
-{
-       u32 cctl = 0;
-
-       if (!(dst & PL08X_AHB1) || ((dst & PL08X_AHB2) && (src & PL08X_AHB1)))
-               cctl |= PL080_CONTROL_DST_AHB2;
-       if (!(src & PL08X_AHB1) || ((src & PL08X_AHB2) && !(dst & PL08X_AHB2)))
-               cctl |= PL080_CONTROL_SRC_AHB2;
-
-       return cctl;
-}
-
 static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan,
        unsigned long flags)
 {
@@ -1338,8 +1348,8 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
        txd->cctl |= PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR;
 
        if (pl08x->vd->dualmaster)
-               txd->cctl |= pl08x_select_bus(pl08x,
-                                       pl08x->mem_buses, pl08x->mem_buses);
+               txd->cctl |= pl08x_select_bus(pl08x->mem_buses,
+                                             pl08x->mem_buses);
 
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
@@ -1356,7 +1366,6 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        struct pl08x_dma_chan *plchan = to_pl08x_chan(chan);
        struct pl08x_driver_data *pl08x = plchan->host;
        struct pl08x_txd *txd;
-       u8 src_buses, dst_buses;
        int ret;
 
        /*
@@ -1390,42 +1399,22 @@ static struct dma_async_tx_descriptor *pl08x_prep_slave_sg(
        txd->direction = direction;
        txd->len = sgl->length;
 
-       txd->cctl = plchan->cd->cctl &
-                       ~(PL080_CONTROL_SRC_AHB2 | PL080_CONTROL_DST_AHB2 |
-                         PL080_CONTROL_SRC_INCR | PL080_CONTROL_DST_INCR |
-                         PL080_CONTROL_PROT_MASK);
-
-       /* Access the cell in privileged mode, non-bufferable, non-cacheable */
-       txd->cctl |= PL080_CONTROL_PROT_SYS;
-
        if (direction == DMA_TO_DEVICE) {
                txd->ccfg |= PL080_FLOW_MEM2PER << PL080_CONFIG_FLOW_CONTROL_SHIFT;
-               txd->cctl |= PL080_CONTROL_SRC_INCR;
+               txd->cctl = plchan->dst_cctl;
                txd->src_addr = sgl->dma_address;
-               if (plchan->runtime_addr)
-                       txd->dst_addr = plchan->runtime_addr;
-               else
-                       txd->dst_addr = plchan->cd->addr;
-               src_buses = pl08x->mem_buses;
-               dst_buses = plchan->cd->periph_buses;
+               txd->dst_addr = plchan->dst_addr;
        } else if (direction == DMA_FROM_DEVICE) {
                txd->ccfg |= PL080_FLOW_PER2MEM << PL080_CONFIG_FLOW_CONTROL_SHIFT;
-               txd->cctl |= PL080_CONTROL_DST_INCR;
-               if (plchan->runtime_addr)
-                       txd->src_addr = plchan->runtime_addr;
-               else
-                       txd->src_addr = plchan->cd->addr;
+               txd->cctl = plchan->src_cctl;
+               txd->src_addr = plchan->src_addr;
                txd->dst_addr = sgl->dma_address;
-               src_buses = plchan->cd->periph_buses;
-               dst_buses = pl08x->mem_buses;
        } else {
                dev_err(&pl08x->adev->dev,
                        "%s direction unsupported\n", __func__);
                return NULL;
        }
 
-       txd->cctl |= pl08x_select_bus(pl08x, src_buses, dst_buses);
-
        ret = pl08x_prep_channel_resources(plchan, txd);
        if (ret)
                return NULL;
@@ -1676,6 +1665,20 @@ static irqreturn_t pl08x_irq(int irq, void *dev)
        return mask ? IRQ_HANDLED : IRQ_NONE;
 }
 
+static void pl08x_dma_slave_init(struct pl08x_dma_chan *chan)
+{
+       u32 cctl = pl08x_cctl(chan->cd->cctl);
+
+       chan->slave = true;
+       chan->name = chan->cd->bus_id;
+       chan->src_addr = chan->cd->addr;
+       chan->dst_addr = chan->cd->addr;
+       chan->src_cctl = cctl | PL080_CONTROL_DST_INCR |
+               pl08x_select_bus(chan->cd->periph_buses, chan->host->mem_buses);
+       chan->dst_cctl = cctl | PL080_CONTROL_SRC_INCR |
+               pl08x_select_bus(chan->host->mem_buses, chan->cd->periph_buses);
+}
+
 /*
  * Initialise the DMAC memcpy/slave channels.
  * Make a local wrapper to hold required data
@@ -1707,9 +1710,8 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
                chan->state = PL08X_CHAN_IDLE;
 
                if (slave) {
-                       chan->slave = true;
-                       chan->name = pl08x->pd->slave_channels[i].bus_id;
                        chan->cd = &pl08x->pd->slave_channels[i];
+                       pl08x_dma_slave_init(chan);
                } else {
                        chan->cd = &pl08x->pd->memcpy_channel;
                        chan->name = kasprintf(GFP_KERNEL, "memcpy%d", i);
index 36144f88d718f383b232b25d168030077f6769d3..6a483eac7b3f2d971cabae0aceaf022a42866558 100644 (file)
@@ -1216,7 +1216,7 @@ static int __init at_dma_probe(struct platform_device *pdev)
        atdma->dma_common.cap_mask = pdata->cap_mask;
        atdma->all_chan_mask = (1 << pdata->nr_channels) - 1;
 
-       size = io->end - io->start + 1;
+       size = resource_size(io);
        if (!request_mem_region(io->start, size, pdev->dev.driver->name)) {
                err = -EBUSY;
                goto err_kfree;
@@ -1362,7 +1362,7 @@ static int __exit at_dma_remove(struct platform_device *pdev)
        atdma->regs = NULL;
 
        io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(io->start, io->end - io->start + 1);
+       release_mem_region(io->start, resource_size(io));
 
        kfree(atdma);
 
index a92d95eac86b204fcb62dd1dafbe8a7150957807..4234f416ef115055cb425822a2c411a73dd41590 100644 (file)
@@ -41,6 +41,8 @@ struct coh901318_desc {
        struct coh901318_lli *lli;
        enum dma_data_direction dir;
        unsigned long flags;
+       u32 head_config;
+       u32 head_ctrl;
 };
 
 struct coh901318_base {
@@ -661,6 +663,9 @@ static struct coh901318_desc *coh901318_queue_start(struct coh901318_chan *cohc)
 
                coh901318_desc_submit(cohc, cohd);
 
+               /* Program the transaction head */
+               coh901318_set_conf(cohc, cohd->head_config);
+               coh901318_set_ctrl(cohc, cohd->head_ctrl);
                coh901318_prep_linked_list(cohc, cohd->lli);
 
                /* start dma job on this channel */
@@ -1091,8 +1096,6 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        } else
                goto err_direction;
 
-       coh901318_set_conf(cohc, config);
-
        /* The dma only supports transmitting packages up to
         * MAX_DMA_PACKET_SIZE. Calculate to total number of
         * dma elemts required to send the entire sg list
@@ -1129,16 +1132,18 @@ coh901318_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (ret)
                goto err_lli_fill;
 
-       /*
-        * Set the default ctrl for the channel to the one from the lli,
-        * things may have changed due to odd buffer alignment etc.
-        */
-       coh901318_set_ctrl(cohc, lli->control);
 
        COH_DBG(coh901318_list_print(cohc, lli));
 
        /* Pick a descriptor to handle this transfer */
        cohd = coh901318_desc_get(cohc);
+       cohd->head_config = config;
+       /*
+        * Set the default head ctrl for the channel to the one from the
+        * lli, things may have changed due to odd buffer alignment
+        * etc.
+        */
+       cohd->head_ctrl = lli->control;
        cohd->dir = direction;
        cohd->flags = flags;
        cohd->desc.tx_submit = coh901318_tx_submit;
index 48694c34d96bd95fe90ebf5eb377877c3851817a..26374b2a55a24dcc49a2400377239a5edc51a07e 100644 (file)
@@ -510,8 +510,8 @@ struct dma_chan *__dma_request_channel(dma_cap_mask_t *mask, dma_filter_fn fn, v
                                         dma_chan_name(chan));
                                list_del_rcu(&device->global_node);
                        } else if (err)
-                               pr_err("dmaengine: failed to get %s: (%d)\n",
-                                      dma_chan_name(chan), err);
+                               pr_debug("dmaengine: failed to get %s: (%d)\n",
+                                        dma_chan_name(chan), err);
                        else
                                break;
                        if (--device->privatecnt == 0)
index 0766c1e53b1d760bf08dd01a919abbf3ab9f0eda..5d7a49bd7c2658ec53887781d2063d2046413b35 100644 (file)
@@ -902,7 +902,7 @@ static void ep93xx_dma_free_chan_resources(struct dma_chan *chan)
  *
  * Returns a valid DMA descriptor or %NULL in case of failure.
  */
-struct dma_async_tx_descriptor *
+static struct dma_async_tx_descriptor *
 ep93xx_dma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest,
                           dma_addr_t src, size_t len, unsigned long flags)
 {
index 1eb60ded2f0de21b29bdd672b3609f8a3b7d716b..7bd7e98548cd34e495910f093ee44e821c1de67b 100644 (file)
@@ -1305,8 +1305,10 @@ static int __init sdma_probe(struct platform_device *pdev)
                goto err_request_irq;
 
        sdma->script_addrs = kzalloc(sizeof(*sdma->script_addrs), GFP_KERNEL);
-       if (!sdma->script_addrs)
+       if (!sdma->script_addrs) {
+               ret = -ENOMEM;
                goto err_alloc;
+       }
 
        if (of_id)
                pdev->id_entry = of_id->data;
index f653517ef7445c484580ef140b64315781c05d13..8a3fdd87db97a462ed4738f1c294276ca0817367 100644 (file)
@@ -1351,7 +1351,6 @@ int dma_suspend(struct pci_dev *pci, pm_message_t state)
                        return -EAGAIN;
        }
        device->state = SUSPENDED;
-       pci_set_drvdata(pci, device);
        pci_save_state(pci);
        pci_disable_device(pci);
        pci_set_power_state(pci, PCI_D3hot);
@@ -1380,7 +1379,6 @@ int dma_resume(struct pci_dev *pci)
        }
        device->state = RUNNING;
        iowrite32(REG_BIT0, device->dma_base + DMA_CFG);
-       pci_set_drvdata(pci, device);
        return 0;
 }
 
index fd7d2b308cf2119f53c83107f45d5283e0620322..6815905a772f786b41095d37bc115deec8b9ca2d 100644 (file)
@@ -1706,16 +1706,14 @@ static int __init ipu_probe(struct platform_device *pdev)
                ipu_data.irq_fn, ipu_data.irq_err, ipu_data.irq_base);
 
        /* Remap IPU common registers */
-       ipu_data.reg_ipu = ioremap(mem_ipu->start,
-                                  mem_ipu->end - mem_ipu->start + 1);
+       ipu_data.reg_ipu = ioremap(mem_ipu->start, resource_size(mem_ipu));
        if (!ipu_data.reg_ipu) {
                ret = -ENOMEM;
                goto err_ioremap_ipu;
        }
 
        /* Remap Image Converter and Image DMA Controller registers */
-       ipu_data.reg_ic = ioremap(mem_ic->start,
-                                 mem_ic->end - mem_ic->start + 1);
+       ipu_data.reg_ic = ioremap(mem_ic->start, resource_size(mem_ic));
        if (!ipu_data.reg_ic) {
                ret = -ENOMEM;
                goto err_ioremap_ic;
index 06f9f27dbe7cdfcd1639f3644727d23ed5a20799..9a353c2216d09a1c1a498da4344f31a03f90aad3 100644 (file)
@@ -1304,7 +1304,8 @@ static int mv_xor_shared_probe(struct platform_device *pdev)
        if (!res)
                return -ENODEV;
 
-       msp->xor_base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+       msp->xor_base = devm_ioremap(&pdev->dev, res->start,
+                                    resource_size(res));
        if (!msp->xor_base)
                return -EBUSY;
 
index 88aad4f540026cbe3b653f4e9d40391cf342aaf0..be641cbd36fcb30c16aafaf25f279ecc0064b07a 100644 (file)
@@ -327,10 +327,12 @@ static int mxs_dma_alloc_chan_resources(struct dma_chan *chan)
 
        memset(mxs_chan->ccw, 0, PAGE_SIZE);
 
-       ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
-                               0, "mxs-dma", mxs_dma);
-       if (ret)
-               goto err_irq;
+       if (mxs_chan->chan_irq != NO_IRQ) {
+               ret = request_irq(mxs_chan->chan_irq, mxs_dma_int_handler,
+                                       0, "mxs-dma", mxs_dma);
+               if (ret)
+                       goto err_irq;
+       }
 
        ret = clk_enable(mxs_dma->clk);
        if (ret)
@@ -535,6 +537,7 @@ static int mxs_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        switch (cmd) {
        case DMA_TERMINATE_ALL:
                mxs_dma_disable_chan(mxs_chan);
+               mxs_dma_reset_chan(mxs_chan);
                break;
        case DMA_PAUSE:
                mxs_dma_pause_chan(mxs_chan);
@@ -707,6 +710,8 @@ static struct platform_device_id mxs_dma_type[] = {
        }, {
                .name = "mxs-dma-apbx",
                .driver_data = MXS_DMA_APBX,
+       }, {
+               /* end of list */
        }
 };
 
index ff5b38f9d45bc98249ddf2a648a5b338f63f3496..1ac8d4b580b732e1cad7a25cfc32a7a068f67c92 100644 (file)
@@ -45,7 +45,8 @@
 #define DMA_STATUS_MASK_BITS           0x3
 #define DMA_STATUS_SHIFT_BITS          16
 #define DMA_STATUS_IRQ(x)              (0x1 << (x))
-#define DMA_STATUS_ERR(x)              (0x1 << ((x) + 8))
+#define DMA_STATUS0_ERR(x)             (0x1 << ((x) + 8))
+#define DMA_STATUS2_ERR(x)             (0x1 << (x))
 
 #define DMA_DESC_WIDTH_SHIFT_BITS      12
 #define DMA_DESC_WIDTH_1_BYTE          (0x3 << DMA_DESC_WIDTH_SHIFT_BITS)
@@ -61,6 +62,9 @@
 
 #define MAX_CHAN_NR                    8
 
+#define DMA_MASK_CTL0_MODE     0x33333333
+#define DMA_MASK_CTL2_MODE     0x00003333
+
 static unsigned int init_nr_desc_per_channel = 64;
 module_param(init_nr_desc_per_channel, uint, 0644);
 MODULE_PARM_DESC(init_nr_desc_per_channel,
@@ -133,6 +137,7 @@ struct pch_dma {
 #define PCH_DMA_CTL3   0x0C
 #define PCH_DMA_STS0   0x10
 #define PCH_DMA_STS1   0x14
+#define PCH_DMA_STS2   0x18
 
 #define dma_readl(pd, name) \
        readl((pd)->membase + PCH_DMA_##name)
@@ -183,13 +188,19 @@ static void pdc_enable_irq(struct dma_chan *chan, int enable)
 {
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       int pos;
+
+       if (chan->chan_id < 8)
+               pos = chan->chan_id;
+       else
+               pos = chan->chan_id + 8;
 
        val = dma_readl(pd, CTL2);
 
        if (enable)
-               val |= 0x1 << chan->chan_id;
+               val |= 0x1 << pos;
        else
-               val &= ~(0x1 << chan->chan_id);
+               val &= ~(0x1 << pos);
 
        dma_writel(pd, CTL2, val);
 
@@ -202,10 +213,17 @@ static void pdc_set_dir(struct dma_chan *chan)
        struct pch_dma_chan *pd_chan = to_pd_chan(chan);
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       u32 mask_mode;
+       u32 mask_ctl;
 
        if (chan->chan_id < 8) {
                val = dma_readl(pd, CTL0);
 
+               mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+                                       (DMA_CTL0_BITS_PER_CH * chan->chan_id);
+               mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                      (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               val &= mask_mode;
                if (pd_chan->dir == DMA_TO_DEVICE)
                        val |= 0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
                                       DMA_CTL0_DIR_SHIFT_BITS);
@@ -213,18 +231,24 @@ static void pdc_set_dir(struct dma_chan *chan)
                        val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +
                                         DMA_CTL0_DIR_SHIFT_BITS));
 
+               val |= mask_ctl;
                dma_writel(pd, CTL0, val);
        } else {
                int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
                val = dma_readl(pd, CTL3);
 
+               mask_mode = DMA_CTL0_MODE_MASK_BITS <<
+                                               (DMA_CTL0_BITS_PER_CH * ch);
+               mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                                (DMA_CTL0_BITS_PER_CH * ch));
+               val &= mask_mode;
                if (pd_chan->dir == DMA_TO_DEVICE)
                        val |= 0x1 << (DMA_CTL0_BITS_PER_CH * ch +
                                       DMA_CTL0_DIR_SHIFT_BITS);
                else
                        val &= ~(0x1 << (DMA_CTL0_BITS_PER_CH * ch +
                                         DMA_CTL0_DIR_SHIFT_BITS));
-
+               val |= mask_ctl;
                dma_writel(pd, CTL3, val);
        }
 
@@ -236,33 +260,37 @@ static void pdc_set_mode(struct dma_chan *chan, u32 mode)
 {
        struct pch_dma *pd = to_pd(chan->device);
        u32 val;
+       u32 mask_ctl;
+       u32 mask_dir;
 
        if (chan->chan_id < 8) {
+               mask_ctl = DMA_MASK_CTL0_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                          (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * chan->chan_id +\
+                                DMA_CTL0_DIR_SHIFT_BITS);
                val = dma_readl(pd, CTL0);
-
-               val &= ~(DMA_CTL0_MODE_MASK_BITS <<
-                       (DMA_CTL0_BITS_PER_CH * chan->chan_id));
+               val &= mask_dir;
                val |= mode << (DMA_CTL0_BITS_PER_CH * chan->chan_id);
-
+               val |= mask_ctl;
                dma_writel(pd, CTL0, val);
        } else {
                int ch = chan->chan_id - 8; /* ch8-->0 ch9-->1 ... ch11->3 */
-
+               mask_ctl = DMA_MASK_CTL2_MODE & ~(DMA_CTL0_MODE_MASK_BITS <<
+                                                (DMA_CTL0_BITS_PER_CH * ch));
+               mask_dir = 1 << (DMA_CTL0_BITS_PER_CH * ch +\
+                                DMA_CTL0_DIR_SHIFT_BITS);
                val = dma_readl(pd, CTL3);
-
-               val &= ~(DMA_CTL0_MODE_MASK_BITS <<
-                       (DMA_CTL0_BITS_PER_CH * ch));
+               val &= mask_dir;
                val |= mode << (DMA_CTL0_BITS_PER_CH * ch);
-
+               val |= mask_ctl;
                dma_writel(pd, CTL3, val);
-
        }
 
        dev_dbg(chan2dev(chan), "pdc_set_mode: chan %d -> %x\n",
                chan->chan_id, val);
 }
 
-static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
+static u32 pdc_get_status0(struct pch_dma_chan *pd_chan)
 {
        struct pch_dma *pd = to_pd(pd_chan->chan.device);
        u32 val;
@@ -272,9 +300,27 @@ static u32 pdc_get_status(struct pch_dma_chan *pd_chan)
                        DMA_STATUS_BITS_PER_CH * pd_chan->chan.chan_id));
 }
 
+static u32 pdc_get_status2(struct pch_dma_chan *pd_chan)
+{
+       struct pch_dma *pd = to_pd(pd_chan->chan.device);
+       u32 val;
+
+       val = dma_readl(pd, STS2);
+       return DMA_STATUS_MASK_BITS & (val >> (DMA_STATUS_SHIFT_BITS +
+                       DMA_STATUS_BITS_PER_CH * (pd_chan->chan.chan_id - 8)));
+}
+
 static bool pdc_is_idle(struct pch_dma_chan *pd_chan)
 {
-       if (pdc_get_status(pd_chan) == DMA_STATUS_IDLE)
+       u32 sts;
+
+       if (pd_chan->chan.chan_id < 8)
+               sts = pdc_get_status0(pd_chan);
+       else
+               sts = pdc_get_status2(pd_chan);
+
+
+       if (sts == DMA_STATUS_IDLE)
                return true;
        else
                return false;
@@ -495,11 +541,11 @@ static int pd_alloc_chan_resources(struct dma_chan *chan)
                list_add_tail(&desc->desc_node, &tmp_list);
        }
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        list_splice(&tmp_list, &pd_chan->free_list);
        pd_chan->descs_allocated = i;
        pd_chan->completed_cookie = chan->cookie = 1;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        pdc_enable_irq(chan, 1);
 
@@ -517,10 +563,10 @@ static void pd_free_chan_resources(struct dma_chan *chan)
        BUG_ON(!list_empty(&pd_chan->active_list));
        BUG_ON(!list_empty(&pd_chan->queue));
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        list_splice_init(&pd_chan->free_list, &tmp_list);
        pd_chan->descs_allocated = 0;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        list_for_each_entry_safe(desc, _d, &tmp_list, desc_node)
                pci_pool_free(pd->pool, desc, desc->txd.phys);
@@ -536,10 +582,10 @@ static enum dma_status pd_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        dma_cookie_t last_completed;
        int ret;
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
        last_completed = pd_chan->completed_cookie;
        last_used = chan->cookie;
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        ret = dma_async_is_complete(cookie, last_completed, last_used);
 
@@ -654,7 +700,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        if (cmd != DMA_TERMINATE_ALL)
                return -ENXIO;
 
-       spin_lock_bh(&pd_chan->lock);
+       spin_lock_irq(&pd_chan->lock);
 
        pdc_set_mode(&pd_chan->chan, DMA_CTL0_DISABLE);
 
@@ -664,7 +710,7 @@ static int pd_device_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        list_for_each_entry_safe(desc, _d, &list, desc_node)
                pdc_chain_complete(pd_chan, desc);
 
-       spin_unlock_bh(&pd_chan->lock);
+       spin_unlock_irq(&pd_chan->lock);
 
        return 0;
 }
@@ -693,30 +739,45 @@ static irqreturn_t pd_irq(int irq, void *devid)
        struct pch_dma *pd = (struct pch_dma *)devid;
        struct pch_dma_chan *pd_chan;
        u32 sts0;
+       u32 sts2;
        int i;
-       int ret = IRQ_NONE;
+       int ret0 = IRQ_NONE;
+       int ret2 = IRQ_NONE;
 
        sts0 = dma_readl(pd, STS0);
+       sts2 = dma_readl(pd, STS2);
 
        dev_dbg(pd->dma.dev, "pd_irq sts0: %x\n", sts0);
 
        for (i = 0; i < pd->dma.chancnt; i++) {
                pd_chan = &pd->channels[i];
 
-               if (sts0 & DMA_STATUS_IRQ(i)) {
-                       if (sts0 & DMA_STATUS_ERR(i))
-                               set_bit(0, &pd_chan->err_status);
+               if (i < 8) {
+                       if (sts0 & DMA_STATUS_IRQ(i)) {
+                               if (sts0 & DMA_STATUS0_ERR(i))
+                                       set_bit(0, &pd_chan->err_status);
 
-                       tasklet_schedule(&pd_chan->tasklet);
-                       ret = IRQ_HANDLED;
-               }
+                               tasklet_schedule(&pd_chan->tasklet);
+                               ret0 = IRQ_HANDLED;
+                       }
+               } else {
+                       if (sts2 & DMA_STATUS_IRQ(i - 8)) {
+                               if (sts2 & DMA_STATUS2_ERR(i))
+                                       set_bit(0, &pd_chan->err_status);
 
+                               tasklet_schedule(&pd_chan->tasklet);
+                               ret2 = IRQ_HANDLED;
+                       }
+               }
        }
 
        /* clear interrupt bits in status register */
-       dma_writel(pd, STS0, sts0);
+       if (ret0)
+               dma_writel(pd, STS0, sts0);
+       if (ret2)
+               dma_writel(pd, STS2, sts2);
 
-       return ret;
+       return ret0 | ret2;
 }
 
 #ifdef CONFIG_PM
index 6abe1ec1f2ce853300524f46ed5ebd0a0d226961..00eee59e8b3357929a3a42f4861921f8ca2b858b 100644 (file)
@@ -82,7 +82,7 @@ struct dma_pl330_dmac {
        spinlock_t pool_lock;
 
        /* Peripheral channels connected to this DMAC */
-       struct dma_pl330_chan peripherals[0]; /* keep at end */
+       struct dma_pl330_chan *peripherals; /* keep at end */
 };
 
 struct dma_pl330_desc {
@@ -451,8 +451,13 @@ static struct dma_pl330_desc *pl330_get_desc(struct dma_pl330_chan *pch)
        desc->txd.cookie = 0;
        async_tx_ack(&desc->txd);
 
-       desc->req.rqtype = peri->rqtype;
-       desc->req.peri = peri->peri_id;
+       if (peri) {
+               desc->req.rqtype = peri->rqtype;
+               desc->req.peri = peri->peri_id;
+       } else {
+               desc->req.rqtype = MEMTOMEM;
+               desc->req.peri = 0;
+       }
 
        dma_async_tx_descriptor_init(&desc->txd, &pch->chan);
 
@@ -529,10 +534,10 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
        struct pl330_info *pi;
        int burst;
 
-       if (unlikely(!pch || !len || !peri))
+       if (unlikely(!pch || !len))
                return NULL;
 
-       if (peri->rqtype != MEMTOMEM)
+       if (peri && peri->rqtype != MEMTOMEM)
                return NULL;
 
        pi = &pch->dmac->pif;
@@ -577,7 +582,7 @@ pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        int i, burst_size;
        dma_addr_t addr;
 
-       if (unlikely(!pch || !sgl || !sg_len))
+       if (unlikely(!pch || !sgl || !sg_len || !peri))
                return NULL;
 
        /* Make sure the direction is consistent */
@@ -666,17 +671,12 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        struct dma_device *pd;
        struct resource *res;
        int i, ret, irq;
+       int num_chan;
 
        pdat = adev->dev.platform_data;
 
-       if (!pdat || !pdat->nr_valid_peri) {
-               dev_err(&adev->dev, "platform data missing\n");
-               return -ENODEV;
-       }
-
        /* Allocate a new DMAC and its Channels */
-       pdmac = kzalloc(pdat->nr_valid_peri * sizeof(*pch)
-                               + sizeof(*pdmac), GFP_KERNEL);
+       pdmac = kzalloc(sizeof(*pdmac), GFP_KERNEL);
        if (!pdmac) {
                dev_err(&adev->dev, "unable to allocate mem\n");
                return -ENOMEM;
@@ -685,7 +685,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        pi = &pdmac->pif;
        pi->dev = &adev->dev;
        pi->pl330_data = NULL;
-       pi->mcbufsz = pdat->mcbuf_sz;
+       pi->mcbufsz = pdat ? pdat->mcbuf_sz : 0;
 
        res = &adev->res;
        request_mem_region(res->start, resource_size(res), "dma-pl330");
@@ -717,27 +717,35 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
        INIT_LIST_HEAD(&pd->channels);
 
        /* Initialize channel parameters */
-       for (i = 0; i < pdat->nr_valid_peri; i++) {
-               struct dma_pl330_peri *peri = &pdat->peri[i];
-               pch = &pdmac->peripherals[i];
+       num_chan = max(pdat ? pdat->nr_valid_peri : 0, (u8)pi->pcfg.num_chan);
+       pdmac->peripherals = kzalloc(num_chan * sizeof(*pch), GFP_KERNEL);
 
-               switch (peri->rqtype) {
-               case MEMTOMEM:
+       for (i = 0; i < num_chan; i++) {
+               pch = &pdmac->peripherals[i];
+               if (pdat) {
+                       struct dma_pl330_peri *peri = &pdat->peri[i];
+
+                       switch (peri->rqtype) {
+                       case MEMTOMEM:
+                               dma_cap_set(DMA_MEMCPY, pd->cap_mask);
+                               break;
+                       case MEMTODEV:
+                       case DEVTOMEM:
+                               dma_cap_set(DMA_SLAVE, pd->cap_mask);
+                               break;
+                       default:
+                               dev_err(&adev->dev, "DEVTODEV Not Supported\n");
+                               continue;
+                       }
+                       pch->chan.private = peri;
+               } else {
                        dma_cap_set(DMA_MEMCPY, pd->cap_mask);
-                       break;
-               case MEMTODEV:
-               case DEVTOMEM:
-                       dma_cap_set(DMA_SLAVE, pd->cap_mask);
-                       break;
-               default:
-                       dev_err(&adev->dev, "DEVTODEV Not Supported\n");
-                       continue;
+                       pch->chan.private = NULL;
                }
 
                INIT_LIST_HEAD(&pch->work_list);
                spin_lock_init(&pch->lock);
                pch->pl330_chid = NULL;
-               pch->chan.private = peri;
                pch->chan.device = pd;
                pch->chan.chan_id = i;
                pch->dmac = pdmac;
index 29d1addbe0cf039ce9beb0bf7660022cbbda1e4e..cd3a7c726bf87bef330f882981afc2fc42be964e 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/err.h>
+#include <linux/amba/bus.h>
 
 #include <plat/ste_dma40.h>
 
@@ -45,9 +46,6 @@
 #define D40_ALLOC_PHY          (1 << 30)
 #define D40_ALLOC_LOG_FREE     0
 
-/* Hardware designer of the block */
-#define D40_HW_DESIGNER 0x8
-
 /**
  * enum 40_command - The different commands and/or statuses.
  *
@@ -186,6 +184,8 @@ struct d40_base;
  * @log_def: Default logical channel settings.
  * @lcla: Space for one dst src pair for logical channel transfers.
  * @lcpa: Pointer to dst and src lcpa settings.
+ * @runtime_addr: runtime configured address.
+ * @runtime_direction: runtime configured direction.
  *
  * This struct can either "be" a logical or a physical channel.
  */
@@ -200,6 +200,7 @@ struct d40_chan {
        struct dma_chan                  chan;
        struct tasklet_struct            tasklet;
        struct list_head                 client;
+       struct list_head                 pending_queue;
        struct list_head                 active;
        struct list_head                 queue;
        struct stedma40_chan_cfg         dma_cfg;
@@ -645,7 +646,20 @@ static struct d40_desc *d40_first_active_get(struct d40_chan *d40c)
 
 static void d40_desc_queue(struct d40_chan *d40c, struct d40_desc *desc)
 {
-       list_add_tail(&desc->node, &d40c->queue);
+       list_add_tail(&desc->node, &d40c->pending_queue);
+}
+
+static struct d40_desc *d40_first_pending(struct d40_chan *d40c)
+{
+       struct d40_desc *d;
+
+       if (list_empty(&d40c->pending_queue))
+               return NULL;
+
+       d = list_first_entry(&d40c->pending_queue,
+                            struct d40_desc,
+                            node);
+       return d;
 }
 
 static struct d40_desc *d40_first_queued(struct d40_chan *d40c)
@@ -802,6 +816,11 @@ static void d40_term_all(struct d40_chan *d40c)
                d40_desc_free(d40c, d40d);
        }
 
+       /* Release pending descriptors */
+       while ((d40d = d40_first_pending(d40c))) {
+               d40_desc_remove(d40d);
+               d40_desc_free(d40c, d40d);
+       }
 
        d40c->pending_tx = 0;
        d40c->busy = false;
@@ -2092,7 +2111,7 @@ dma40_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t dma_addr,
        struct scatterlist *sg;
        int i;
 
-       sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_KERNEL);
+       sg = kcalloc(periods + 1, sizeof(struct scatterlist), GFP_NOWAIT);
        for (i = 0; i < periods; i++) {
                sg_dma_address(&sg[i]) = dma_addr;
                sg_dma_len(&sg[i]) = period_len;
@@ -2152,24 +2171,87 @@ static void d40_issue_pending(struct dma_chan *chan)
 
        spin_lock_irqsave(&d40c->lock, flags);
 
-       /* Busy means that pending jobs are already being processed */
+       list_splice_tail_init(&d40c->pending_queue, &d40c->queue);
+
+       /* Busy means that queued jobs are already being processed */
        if (!d40c->busy)
                (void) d40_queue_start(d40c);
 
        spin_unlock_irqrestore(&d40c->lock, flags);
 }
 
+static int
+dma40_config_to_halfchannel(struct d40_chan *d40c,
+                           struct stedma40_half_channel_info *info,
+                           enum dma_slave_buswidth width,
+                           u32 maxburst)
+{
+       enum stedma40_periph_data_width addr_width;
+       int psize;
+
+       switch (width) {
+       case DMA_SLAVE_BUSWIDTH_1_BYTE:
+               addr_width = STEDMA40_BYTE_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_2_BYTES:
+               addr_width = STEDMA40_HALFWORD_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_4_BYTES:
+               addr_width = STEDMA40_WORD_WIDTH;
+               break;
+       case DMA_SLAVE_BUSWIDTH_8_BYTES:
+               addr_width = STEDMA40_DOUBLEWORD_WIDTH;
+               break;
+       default:
+               dev_err(d40c->base->dev,
+                       "illegal peripheral address width "
+                       "requested (%d)\n",
+                       width);
+               return -EINVAL;
+       }
+
+       if (chan_is_logical(d40c)) {
+               if (maxburst >= 16)
+                       psize = STEDMA40_PSIZE_LOG_16;
+               else if (maxburst >= 8)
+                       psize = STEDMA40_PSIZE_LOG_8;
+               else if (maxburst >= 4)
+                       psize = STEDMA40_PSIZE_LOG_4;
+               else
+                       psize = STEDMA40_PSIZE_LOG_1;
+       } else {
+               if (maxburst >= 16)
+                       psize = STEDMA40_PSIZE_PHY_16;
+               else if (maxburst >= 8)
+                       psize = STEDMA40_PSIZE_PHY_8;
+               else if (maxburst >= 4)
+                       psize = STEDMA40_PSIZE_PHY_4;
+               else
+                       psize = STEDMA40_PSIZE_PHY_1;
+       }
+
+       info->data_width = addr_width;
+       info->psize = psize;
+       info->flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+
+       return 0;
+}
+
 /* Runtime reconfiguration extension */
-static void d40_set_runtime_config(struct dma_chan *chan,
-                              struct dma_slave_config *config)
+static int d40_set_runtime_config(struct dma_chan *chan,
+                                 struct dma_slave_config *config)
 {
        struct d40_chan *d40c = container_of(chan, struct d40_chan, chan);
        struct stedma40_chan_cfg *cfg = &d40c->dma_cfg;
-       enum dma_slave_buswidth config_addr_width;
+       enum dma_slave_buswidth src_addr_width, dst_addr_width;
        dma_addr_t config_addr;
-       u32 config_maxburst;
-       enum stedma40_periph_data_width addr_width;
-       int psize;
+       u32 src_maxburst, dst_maxburst;
+       int ret;
+
+       src_addr_width = config->src_addr_width;
+       src_maxburst = config->src_maxburst;
+       dst_addr_width = config->dst_addr_width;
+       dst_maxburst = config->dst_maxburst;
 
        if (config->direction == DMA_FROM_DEVICE) {
                dma_addr_t dev_addr_rx =
@@ -2188,8 +2270,11 @@ static void d40_set_runtime_config(struct dma_chan *chan,
                                cfg->dir);
                cfg->dir = STEDMA40_PERIPH_TO_MEM;
 
-               config_addr_width = config->src_addr_width;
-               config_maxburst = config->src_maxburst;
+               /* Configure the memory side */
+               if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+                       dst_addr_width = src_addr_width;
+               if (dst_maxburst == 0)
+                       dst_maxburst = src_maxburst;
 
        } else if (config->direction == DMA_TO_DEVICE) {
                dma_addr_t dev_addr_tx =
@@ -2208,68 +2293,39 @@ static void d40_set_runtime_config(struct dma_chan *chan,
                                cfg->dir);
                cfg->dir = STEDMA40_MEM_TO_PERIPH;
 
-               config_addr_width = config->dst_addr_width;
-               config_maxburst = config->dst_maxburst;
-
+               /* Configure the memory side */
+               if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED)
+                       src_addr_width = dst_addr_width;
+               if (src_maxburst == 0)
+                       src_maxburst = dst_maxburst;
        } else {
                dev_err(d40c->base->dev,
                        "unrecognized channel direction %d\n",
                        config->direction);
-               return;
+               return -EINVAL;
        }
 
-       switch (config_addr_width) {
-       case DMA_SLAVE_BUSWIDTH_1_BYTE:
-               addr_width = STEDMA40_BYTE_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_2_BYTES:
-               addr_width = STEDMA40_HALFWORD_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_4_BYTES:
-               addr_width = STEDMA40_WORD_WIDTH;
-               break;
-       case DMA_SLAVE_BUSWIDTH_8_BYTES:
-               addr_width = STEDMA40_DOUBLEWORD_WIDTH;
-               break;
-       default:
+       if (src_maxburst * src_addr_width != dst_maxburst * dst_addr_width) {
                dev_err(d40c->base->dev,
-                       "illegal peripheral address width "
-                       "requested (%d)\n",
-                       config->src_addr_width);
-               return;
+                       "src/dst width/maxburst mismatch: %d*%d != %d*%d\n",
+                       src_maxburst,
+                       src_addr_width,
+                       dst_maxburst,
+                       dst_addr_width);
+               return -EINVAL;
        }
 
-       if (chan_is_logical(d40c)) {
-               if (config_maxburst >= 16)
-                       psize = STEDMA40_PSIZE_LOG_16;
-               else if (config_maxburst >= 8)
-                       psize = STEDMA40_PSIZE_LOG_8;
-               else if (config_maxburst >= 4)
-                       psize = STEDMA40_PSIZE_LOG_4;
-               else
-                       psize = STEDMA40_PSIZE_LOG_1;
-       } else {
-               if (config_maxburst >= 16)
-                       psize = STEDMA40_PSIZE_PHY_16;
-               else if (config_maxburst >= 8)
-                       psize = STEDMA40_PSIZE_PHY_8;
-               else if (config_maxburst >= 4)
-                       psize = STEDMA40_PSIZE_PHY_4;
-               else if (config_maxburst >= 2)
-                       psize = STEDMA40_PSIZE_PHY_2;
-               else
-                       psize = STEDMA40_PSIZE_PHY_1;
-       }
+       ret = dma40_config_to_halfchannel(d40c, &cfg->src_info,
+                                         src_addr_width,
+                                         src_maxburst);
+       if (ret)
+               return ret;
 
-       /* Set up all the endpoint configs */
-       cfg->src_info.data_width = addr_width;
-       cfg->src_info.psize = psize;
-       cfg->src_info.big_endian = false;
-       cfg->src_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
-       cfg->dst_info.data_width = addr_width;
-       cfg->dst_info.psize = psize;
-       cfg->dst_info.big_endian = false;
-       cfg->dst_info.flow_ctrl = STEDMA40_NO_FLOW_CTRL;
+       ret = dma40_config_to_halfchannel(d40c, &cfg->dst_info,
+                                         dst_addr_width,
+                                         dst_maxburst);
+       if (ret)
+               return ret;
 
        /* Fill in register values */
        if (chan_is_logical(d40c))
@@ -2282,12 +2338,14 @@ static void d40_set_runtime_config(struct dma_chan *chan,
        d40c->runtime_addr = config_addr;
        d40c->runtime_direction = config->direction;
        dev_dbg(d40c->base->dev,
-               "configured channel %s for %s, data width %d, "
-               "maxburst %d bytes, LE, no flow control\n",
+               "configured channel %s for %s, data width %d/%d, "
+               "maxburst %d/%d elements, LE, no flow control\n",
                dma_chan_name(chan),
                (config->direction == DMA_FROM_DEVICE) ? "RX" : "TX",
-               config_addr_width,
-               config_maxburst);
+               src_addr_width, dst_addr_width,
+               src_maxburst, dst_maxburst);
+
+       return 0;
 }
 
 static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
@@ -2308,9 +2366,8 @@ static int d40_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
        case DMA_RESUME:
                return d40_resume(d40c);
        case DMA_SLAVE_CONFIG:
-               d40_set_runtime_config(chan,
+               return d40_set_runtime_config(chan,
                        (struct dma_slave_config *) arg);
-               return 0;
        default:
                break;
        }
@@ -2341,6 +2398,7 @@ static void __init d40_chan_init(struct d40_base *base, struct dma_device *dma,
 
                INIT_LIST_HEAD(&d40c->active);
                INIT_LIST_HEAD(&d40c->queue);
+               INIT_LIST_HEAD(&d40c->pending_queue);
                INIT_LIST_HEAD(&d40c->client);
 
                tasklet_init(&d40c->tasklet, dma_tasklet,
@@ -2502,25 +2560,6 @@ static int __init d40_phy_res_init(struct d40_base *base)
 
 static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
 {
-       static const struct d40_reg_val dma_id_regs[] = {
-               /* Peripheral Id */
-               { .reg = D40_DREG_PERIPHID0, .val = 0x0040},
-               { .reg = D40_DREG_PERIPHID1, .val = 0x0000},
-               /*
-                * D40_DREG_PERIPHID2 Depends on HW revision:
-                *  DB8500ed has 0x0008,
-                *  ? has 0x0018,
-                *  DB8500v1 has 0x0028
-                *  DB8500v2 has 0x0038
-                */
-               { .reg = D40_DREG_PERIPHID3, .val = 0x0000},
-
-               /* PCell Id */
-               { .reg = D40_DREG_CELLID0, .val = 0x000d},
-               { .reg = D40_DREG_CELLID1, .val = 0x00f0},
-               { .reg = D40_DREG_CELLID2, .val = 0x0005},
-               { .reg = D40_DREG_CELLID3, .val = 0x00b1}
-       };
        struct stedma40_platform_data *plat_data;
        struct clk *clk = NULL;
        void __iomem *virtbase = NULL;
@@ -2529,8 +2568,9 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        int num_log_chans = 0;
        int num_phy_chans;
        int i;
-       u32 val;
-       u32 rev;
+       u32 pid;
+       u32 cid;
+       u8 rev;
 
        clk = clk_get(&pdev->dev, NULL);
 
@@ -2554,32 +2594,32 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev)
        if (!virtbase)
                goto failure;
 
-       /* HW version check */
-       for (i = 0; i < ARRAY_SIZE(dma_id_regs); i++) {
-               if (dma_id_regs[i].val !=
-                   readl(virtbase + dma_id_regs[i].reg)) {
-                       d40_err(&pdev->dev,
-                               "Unknown hardware! Expected 0x%x at 0x%x but got 0x%x\n",
-                               dma_id_regs[i].val,
-                               dma_id_regs[i].reg,
-                               readl(virtbase + dma_id_regs[i].reg));
-                       goto failure;
-               }
-       }
+       /* This is just a regular AMBA PrimeCell ID actually */
+       for (pid = 0, i = 0; i < 4; i++)
+               pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i)
+                       & 255) << (i * 8);
+       for (cid = 0, i = 0; i < 4; i++)
+               cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i)
+                       & 255) << (i * 8);
 
-       /* Get silicon revision and designer */
-       val = readl(virtbase + D40_DREG_PERIPHID2);
-
-       if ((val & D40_DREG_PERIPHID2_DESIGNER_MASK) !=
-           D40_HW_DESIGNER) {
+       if (cid != AMBA_CID) {
+               d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n");
+               goto failure;
+       }
+       if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) {
                d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n",
-                       val & D40_DREG_PERIPHID2_DESIGNER_MASK,
-                       D40_HW_DESIGNER);
+                       AMBA_MANF_BITS(pid),
+                       AMBA_VENDOR_ST);
                goto failure;
        }
-
-       rev = (val & D40_DREG_PERIPHID2_REV_MASK) >>
-               D40_DREG_PERIPHID2_REV_POS;
+       /*
+        * HW revision:
+        * DB8500ed has revision 0
+        * ? has revision 1
+        * DB8500v1 has revision 2
+        * DB8500v2 has revision 3
+        */
+       rev = AMBA_REV_BITS(pid);
 
        /* The number of physical channels on this HW */
        num_phy_chans = 4 * (readl(virtbase + D40_DREG_ICFG) & 0x7) + 4;
index 195ee65ee7f3513111f212c3b51a69bd92d96a67..b44c455158de3461f31ee5a0b0594cb136b2ad6a 100644 (file)
 #define D40_DREG_PERIPHID0     0xFE0
 #define D40_DREG_PERIPHID1     0xFE4
 #define D40_DREG_PERIPHID2     0xFE8
-#define D40_DREG_PERIPHID2_REV_POS 4
-#define D40_DREG_PERIPHID2_REV_MASK (0xf << D40_DREG_PERIPHID2_REV_POS)
-#define D40_DREG_PERIPHID2_DESIGNER_MASK 0xf
 #define D40_DREG_PERIPHID3     0xFEC
 #define D40_DREG_CELLID0       0xFF0
 #define D40_DREG_CELLID1       0xFF4
index 5f29aafd44624e8ee1ccb9215735b9068013e056..eacb05e6cfb36ec0387c624681bb4ed6ac10f70f 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/kobject.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/pstore.h>
 
 #include <asm/uaccess.h>
 
@@ -89,6 +90,8 @@ MODULE_DESCRIPTION("sysfs interface to EFI Variables");
 MODULE_LICENSE("GPL");
 MODULE_VERSION(EFIVARS_VERSION);
 
+#define DUMP_NAME_LEN 52
+
 /*
  * The maximum size of VariableName + Data = 1024
  * Therefore, it's reasonable to save that much
@@ -119,6 +122,10 @@ struct efivar_attribute {
        ssize_t (*store)(struct efivar_entry *entry, const char *buf, size_t count);
 };
 
+#define PSTORE_EFI_ATTRIBUTES \
+       (EFI_VARIABLE_NON_VOLATILE | \
+        EFI_VARIABLE_BOOTSERVICE_ACCESS | \
+        EFI_VARIABLE_RUNTIME_ACCESS)
 
 #define EFIVAR_ATTR(_name, _mode, _show, _store) \
 struct efivar_attribute efivar_attr_##_name = { \
@@ -141,38 +148,72 @@ efivar_create_sysfs_entry(struct efivars *efivars,
 
 /* Return the number of unicode characters in data */
 static unsigned long
-utf8_strlen(efi_char16_t *data, unsigned long maxlength)
+utf16_strnlen(efi_char16_t *s, size_t maxlength)
 {
        unsigned long length = 0;
 
-       while (*data++ != 0 && length < maxlength)
+       while (*s++ != 0 && length < maxlength)
                length++;
        return length;
 }
 
+static unsigned long
+utf16_strlen(efi_char16_t *s)
+{
+       return utf16_strnlen(s, ~0UL);
+}
+
 /*
  * Return the number of bytes is the length of this string
  * Note: this is NOT the same as the number of unicode characters
  */
 static inline unsigned long
-utf8_strsize(efi_char16_t *data, unsigned long maxlength)
+utf16_strsize(efi_char16_t *data, unsigned long maxlength)
 {
-       return utf8_strlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+       return utf16_strnlen(data, maxlength/sizeof(efi_char16_t)) * sizeof(efi_char16_t);
+}
+
+static inline int
+utf16_strncmp(const efi_char16_t *a, const efi_char16_t *b, size_t len)
+{
+       while (1) {
+               if (len == 0)
+                       return 0;
+               if (*a < *b)
+                       return -1;
+               if (*a > *b)
+                       return 1;
+               if (*a == 0) /* implies *b == 0 */
+                       return 0;
+               a++;
+               b++;
+               len--;
+       }
 }
 
 static efi_status_t
-get_var_data(struct efivars *efivars, struct efi_variable *var)
+get_var_data_locked(struct efivars *efivars, struct efi_variable *var)
 {
        efi_status_t status;
 
-       spin_lock(&efivars->lock);
        var->DataSize = 1024;
        status = efivars->ops->get_variable(var->VariableName,
                                            &var->VendorGuid,
                                            &var->Attributes,
                                            &var->DataSize,
                                            var->Data);
+       return status;
+}
+
+static efi_status_t
+get_var_data(struct efivars *efivars, struct efi_variable *var)
+{
+       efi_status_t status;
+
+       spin_lock(&efivars->lock);
+       status = get_var_data_locked(efivars, var);
        spin_unlock(&efivars->lock);
+
        if (status != EFI_SUCCESS) {
                printk(KERN_WARNING "efivars: get_variable() failed 0x%lx!\n",
                        status);
@@ -387,12 +428,180 @@ static struct kobj_type efivar_ktype = {
        .default_attrs = def_attrs,
 };
 
+static struct pstore_info efi_pstore_info;
+
 static inline void
 efivar_unregister(struct efivar_entry *var)
 {
        kobject_put(&var->kobj);
 }
 
+#ifdef CONFIG_PSTORE
+
+static int efi_pstore_open(struct pstore_info *psi)
+{
+       struct efivars *efivars = psi->data;
+
+       spin_lock(&efivars->lock);
+       efivars->walk_entry = list_first_entry(&efivars->list,
+                                              struct efivar_entry, list);
+       return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+       struct efivars *efivars = psi->data;
+
+       spin_unlock(&efivars->lock);
+       return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+                              struct timespec *timespec, struct pstore_info *psi)
+{
+       efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+       struct efivars *efivars = psi->data;
+       char name[DUMP_NAME_LEN];
+       int i;
+       unsigned int part, size;
+       unsigned long time;
+
+       while (&efivars->walk_entry->list != &efivars->list) {
+               if (!efi_guidcmp(efivars->walk_entry->var.VendorGuid,
+                                vendor)) {
+                       for (i = 0; i < DUMP_NAME_LEN; i++) {
+                               name[i] = efivars->walk_entry->var.VariableName[i];
+                       }
+                       if (sscanf(name, "dump-type%u-%u-%lu", type, &part, &time) == 3) {
+                               *id = part;
+                               timespec->tv_sec = time;
+                               timespec->tv_nsec = 0;
+                               get_var_data_locked(efivars, &efivars->walk_entry->var);
+                               size = efivars->walk_entry->var.DataSize;
+                               memcpy(psi->buf, efivars->walk_entry->var.Data, size);
+                               efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+                                                  struct efivar_entry, list);
+                               return size;
+                       }
+               }
+               efivars->walk_entry = list_entry(efivars->walk_entry->list.next,
+                                                struct efivar_entry, list);
+       }
+       return 0;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, unsigned int part,
+                           size_t size, struct pstore_info *psi)
+{
+       char name[DUMP_NAME_LEN];
+       char stub_name[DUMP_NAME_LEN];
+       efi_char16_t efi_name[DUMP_NAME_LEN];
+       efi_guid_t vendor = LINUX_EFI_CRASH_GUID;
+       struct efivars *efivars = psi->data;
+       struct efivar_entry *entry, *found = NULL;
+       int i;
+
+       sprintf(stub_name, "dump-type%u-%u-", type, part);
+       sprintf(name, "%s%lu", stub_name, get_seconds());
+
+       spin_lock(&efivars->lock);
+
+       for (i = 0; i < DUMP_NAME_LEN; i++)
+               efi_name[i] = stub_name[i];
+
+       /*
+        * Clean up any entries with the same name
+        */
+
+       list_for_each_entry(entry, &efivars->list, list) {
+               get_var_data_locked(efivars, &entry->var);
+
+               if (efi_guidcmp(entry->var.VendorGuid, vendor))
+                       continue;
+               if (utf16_strncmp(entry->var.VariableName, efi_name,
+                                 utf16_strlen(efi_name)))
+                       continue;
+               /* Needs to be a prefix */
+               if (entry->var.VariableName[utf16_strlen(efi_name)] == 0)
+                       continue;
+
+               /* found */
+               found = entry;
+               efivars->ops->set_variable(entry->var.VariableName,
+                                          &entry->var.VendorGuid,
+                                          PSTORE_EFI_ATTRIBUTES,
+                                          0, NULL);
+       }
+
+       if (found)
+               list_del(&found->list);
+
+       for (i = 0; i < DUMP_NAME_LEN; i++)
+               efi_name[i] = name[i];
+
+       efivars->ops->set_variable(efi_name, &vendor, PSTORE_EFI_ATTRIBUTES,
+                                  size, psi->buf);
+
+       spin_unlock(&efivars->lock);
+
+       if (found)
+               efivar_unregister(found);
+
+       if (size)
+               efivar_create_sysfs_entry(efivars,
+                                         utf16_strsize(efi_name,
+                                                       DUMP_NAME_LEN * 2),
+                                         efi_name, &vendor);
+
+       return part;
+};
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+                           struct pstore_info *psi)
+{
+       efi_pstore_write(type, id, 0, psi);
+
+       return 0;
+}
+#else
+static int efi_pstore_open(struct pstore_info *psi)
+{
+       return 0;
+}
+
+static int efi_pstore_close(struct pstore_info *psi)
+{
+       return 0;
+}
+
+static ssize_t efi_pstore_read(u64 *id, enum pstore_type_id *type,
+                              struct timespec *time, struct pstore_info *psi)
+{
+       return -1;
+}
+
+static u64 efi_pstore_write(enum pstore_type_id type, int part, size_t size,
+                           struct pstore_info *psi)
+{
+       return 0;
+}
+
+static int efi_pstore_erase(enum pstore_type_id type, u64 id,
+                           struct pstore_info *psi)
+{
+       return 0;
+}
+#endif
+
+static struct pstore_info efi_pstore_info = {
+       .owner          = THIS_MODULE,
+       .name           = "efi",
+       .open           = efi_pstore_open,
+       .close          = efi_pstore_close,
+       .read           = efi_pstore_read,
+       .write          = efi_pstore_write,
+       .erase          = efi_pstore_erase,
+};
 
 static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
                             struct bin_attribute *bin_attr,
@@ -414,8 +623,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
         * Does this variable already exist?
         */
        list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
-               strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
-               strsize2 = utf8_strsize(new_var->VariableName, 1024);
+               strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+               strsize2 = utf16_strsize(new_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
                        !memcmp(&(search_efivar->var.VariableName),
                                new_var->VariableName, strsize1) &&
@@ -447,8 +656,8 @@ static ssize_t efivar_create(struct file *filp, struct kobject *kobj,
 
        /* Create the entry in sysfs.  Locking is not required here */
        status = efivar_create_sysfs_entry(efivars,
-                                          utf8_strsize(new_var->VariableName,
-                                                       1024),
+                                          utf16_strsize(new_var->VariableName,
+                                                        1024),
                                           new_var->VariableName,
                                           &new_var->VendorGuid);
        if (status) {
@@ -477,8 +686,8 @@ static ssize_t efivar_delete(struct file *filp, struct kobject *kobj,
         * Does this variable already exist?
         */
        list_for_each_entry_safe(search_efivar, n, &efivars->list, list) {
-               strsize1 = utf8_strsize(search_efivar->var.VariableName, 1024);
-               strsize2 = utf8_strsize(del_var->VariableName, 1024);
+               strsize1 = utf16_strsize(search_efivar->var.VariableName, 1024);
+               strsize2 = utf16_strsize(del_var->VariableName, 1024);
                if (strsize1 == strsize2 &&
                        !memcmp(&(search_efivar->var.VariableName),
                                del_var->VariableName, strsize1) &&
@@ -763,6 +972,16 @@ int register_efivars(struct efivars *efivars,
        if (error)
                unregister_efivars(efivars);
 
+       efivars->efi_pstore_info = efi_pstore_info;
+
+       efivars->efi_pstore_info.buf = kmalloc(4096, GFP_KERNEL);
+       if (efivars->efi_pstore_info.buf) {
+               efivars->efi_pstore_info.bufsize = 1024;
+               efivars->efi_pstore_info.data = efivars;
+               mutex_init(&efivars->efi_pstore_info.buf_mutex);
+               pstore_register(&efivars->efi_pstore_info);
+       }
+
 out:
        kfree(variable_name);
 
index 8420129fc5eed67693b99812ab565130b8ad1db7..f75a66e7d312a8e1efed3d2db37ba1c554785e94 100644 (file)
@@ -241,12 +241,13 @@ config DM_MIRROR
          needed for live data migration tools such as 'pvmove'.
 
 config DM_RAID
-       tristate "RAID 4/5/6 target (EXPERIMENTAL)"
+       tristate "RAID 1/4/5/6 target (EXPERIMENTAL)"
        depends on BLK_DEV_DM && EXPERIMENTAL
+       select MD_RAID1
        select MD_RAID456
        select BLK_DEV_MD
        ---help---
-        A dm target that supports RAID4, RAID5 and RAID6 mappings
+        A dm target that supports RAID1, RAID4, RAID5 and RAID6 mappings
 
         A RAID-5 set of N drives with a capacity of C MB per drive provides
         the capacity of C * (N - 1) MB, and protects against a failure
index bae6c4e23d3f7d02798478993c50b976767a81ee..49da55c1528aa01137a61c98d6033c9b84dc2e05 100644 (file)
@@ -30,7 +30,6 @@
 #include <linux/device-mapper.h>
 
 #define DM_MSG_PREFIX "crypt"
-#define MESG_STR(x) x, sizeof(x)
 
 /*
  * context holding the current state of a multi-part conversion
@@ -239,7 +238,7 @@ static int crypt_iv_plain_gen(struct crypt_config *cc, u8 *iv,
                              struct dm_crypt_request *dmreq)
 {
        memset(iv, 0, cc->iv_size);
-       *(u32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
+       *(__le32 *)iv = cpu_to_le32(dmreq->iv_sector & 0xffffffff);
 
        return 0;
 }
@@ -248,7 +247,7 @@ static int crypt_iv_plain64_gen(struct crypt_config *cc, u8 *iv,
                                struct dm_crypt_request *dmreq)
 {
        memset(iv, 0, cc->iv_size);
-       *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+       *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
 
        return 0;
 }
@@ -415,7 +414,7 @@ static int crypt_iv_essiv_gen(struct crypt_config *cc, u8 *iv,
        struct crypto_cipher *essiv_tfm = this_crypt_config(cc)->iv_private;
 
        memset(iv, 0, cc->iv_size);
-       *(u64 *)iv = cpu_to_le64(dmreq->iv_sector);
+       *(__le64 *)iv = cpu_to_le64(dmreq->iv_sector);
        crypto_cipher_encrypt_one(essiv_tfm, iv, iv);
 
        return 0;
@@ -1575,11 +1574,17 @@ bad_mem:
 static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
        struct crypt_config *cc;
-       unsigned int key_size;
+       unsigned int key_size, opt_params;
        unsigned long long tmpll;
        int ret;
+       struct dm_arg_set as;
+       const char *opt_string;
+
+       static struct dm_arg _args[] = {
+               {0, 1, "Invalid number of feature args"},
+       };
 
-       if (argc != 5) {
+       if (argc < 5) {
                ti->error = "Not enough arguments";
                return -EINVAL;
        }
@@ -1648,6 +1653,30 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
        }
        cc->start = tmpll;
 
+       argv += 5;
+       argc -= 5;
+
+       /* Optional parameters */
+       if (argc) {
+               as.argc = argc;
+               as.argv = argv;
+
+               ret = dm_read_arg_group(_args, &as, &opt_params, &ti->error);
+               if (ret)
+                       goto bad;
+
+               opt_string = dm_shift_arg(&as);
+
+               if (opt_params == 1 && opt_string &&
+                   !strcasecmp(opt_string, "allow_discards"))
+                       ti->num_discard_requests = 1;
+               else if (opt_params) {
+                       ret = -EINVAL;
+                       ti->error = "Invalid feature arguments";
+                       goto bad;
+               }
+       }
+
        ret = -ENOMEM;
        cc->io_queue = alloc_workqueue("kcryptd_io",
                                       WQ_NON_REENTRANT|
@@ -1682,9 +1711,16 @@ static int crypt_map(struct dm_target *ti, struct bio *bio,
        struct dm_crypt_io *io;
        struct crypt_config *cc;
 
-       if (bio->bi_rw & REQ_FLUSH) {
+       /*
+        * If bio is REQ_FLUSH or REQ_DISCARD, just bypass crypt queues.
+        * - for REQ_FLUSH device-mapper core ensures that no IO is in-flight
+        * - for REQ_DISCARD caller must use flush if IO ordering matters
+        */
+       if (unlikely(bio->bi_rw & (REQ_FLUSH | REQ_DISCARD))) {
                cc = ti->private;
                bio->bi_bdev = cc->dev->bdev;
+               if (bio_sectors(bio))
+                       bio->bi_sector = cc->start + dm_target_offset(ti, bio->bi_sector);
                return DM_MAPIO_REMAPPED;
        }
 
@@ -1727,6 +1763,10 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
 
                DMEMIT(" %llu %s %llu", (unsigned long long)cc->iv_offset,
                                cc->dev->name, (unsigned long long)cc->start);
+
+               if (ti->num_discard_requests)
+                       DMEMIT(" 1 allow_discards");
+
                break;
        }
        return 0;
@@ -1770,12 +1810,12 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
        if (argc < 2)
                goto error;
 
-       if (!strnicmp(argv[0], MESG_STR("key"))) {
+       if (!strcasecmp(argv[0], "key")) {
                if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {
                        DMWARN("not suspended during key manipulation.");
                        return -EINVAL;
                }
-               if (argc == 3 && !strnicmp(argv[1], MESG_STR("set"))) {
+               if (argc == 3 && !strcasecmp(argv[1], "set")) {
                        ret = crypt_set_key(cc, argv[2]);
                        if (ret)
                                return ret;
@@ -1783,7 +1823,7 @@ static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
                                ret = cc->iv_gen_ops->init(cc);
                        return ret;
                }
-               if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe"))) {
+               if (argc == 2 && !strcasecmp(argv[1], "wipe")) {
                        if (cc->iv_gen_ops && cc->iv_gen_ops->wipe) {
                                ret = cc->iv_gen_ops->wipe(cc);
                                if (ret)
@@ -1823,7 +1863,7 @@ static int crypt_iterate_devices(struct dm_target *ti,
 
 static struct target_type crypt_target = {
        .name   = "crypt",
-       .version = {1, 10, 0},
+       .version = {1, 11, 0},
        .module = THIS_MODULE,
        .ctr    = crypt_ctr,
        .dtr    = crypt_dtr,
index ea790623c30ba0d7522905d6f77f7db95c0cdebd..89f73ca22cfa112e7c215f9ab5c846de80dd5da4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Copyright (C) 2003 Sistina Software (UK) Limited.
- * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
  *
  * This file is released under the GPL.
  */
@@ -15,6 +15,9 @@
 
 #define DM_MSG_PREFIX "flakey"
 
+#define all_corrupt_bio_flags_match(bio, fc)   \
+       (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
+
 /*
  * Flakey: Used for testing only, simulates intermittent,
  * catastrophic device failure.
@@ -25,60 +28,189 @@ struct flakey_c {
        sector_t start;
        unsigned up_interval;
        unsigned down_interval;
+       unsigned long flags;
+       unsigned corrupt_bio_byte;
+       unsigned corrupt_bio_rw;
+       unsigned corrupt_bio_value;
+       unsigned corrupt_bio_flags;
+};
+
+enum feature_flag_bits {
+       DROP_WRITES
 };
 
+static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
+                         struct dm_target *ti)
+{
+       int r;
+       unsigned argc;
+       const char *arg_name;
+
+       static struct dm_arg _args[] = {
+               {0, 6, "Invalid number of feature args"},
+               {1, UINT_MAX, "Invalid corrupt bio byte"},
+               {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
+               {0, UINT_MAX, "Invalid corrupt bio flags mask"},
+       };
+
+       /* No feature arguments supplied. */
+       if (!as->argc)
+               return 0;
+
+       r = dm_read_arg_group(_args, as, &argc, &ti->error);
+       if (r)
+               return r;
+
+       while (argc) {
+               arg_name = dm_shift_arg(as);
+               argc--;
+
+               /*
+                * drop_writes
+                */
+               if (!strcasecmp(arg_name, "drop_writes")) {
+                       if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
+                               ti->error = "Feature drop_writes duplicated";
+                               return -EINVAL;
+                       }
+
+                       continue;
+               }
+
+               /*
+                * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
+                */
+               if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
+                       if (!argc)
+                               ti->error = "Feature corrupt_bio_byte requires parameters";
+
+                       r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       /*
+                        * Direction r or w?
+                        */
+                       arg_name = dm_shift_arg(as);
+                       if (!strcasecmp(arg_name, "w"))
+                               fc->corrupt_bio_rw = WRITE;
+                       else if (!strcasecmp(arg_name, "r"))
+                               fc->corrupt_bio_rw = READ;
+                       else {
+                               ti->error = "Invalid corrupt bio direction (r or w)";
+                               return -EINVAL;
+                       }
+                       argc--;
+
+                       /*
+                        * Value of byte (0-255) to write in place of correct one.
+                        */
+                       r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       /*
+                        * Only corrupt bios with these flags set.
+                        */
+                       r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
+                       if (r)
+                               return r;
+                       argc--;
+
+                       continue;
+               }
+
+               ti->error = "Unrecognised flakey feature requested";
+               return -EINVAL;
+       }
+
+       if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
+               ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 /*
- * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval>
+ * Construct a flakey mapping:
+ * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
+ *
+ *   Feature args:
+ *     [drop_writes]
+ *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
+ *
+ *   Nth_byte starts from 1 for the first byte.
+ *   Direction is r for READ or w for WRITE.
+ *   bio_flags is ignored if 0.
  */
 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 {
+       static struct dm_arg _args[] = {
+               {0, UINT_MAX, "Invalid up interval"},
+               {0, UINT_MAX, "Invalid down interval"},
+       };
+
+       int r;
        struct flakey_c *fc;
-       unsigned long long tmp;
+       unsigned long long tmpll;
+       struct dm_arg_set as;
+       const char *devname;
 
-       if (argc != 4) {
-               ti->error = "dm-flakey: Invalid argument count";
+       as.argc = argc;
+       as.argv = argv;
+
+       if (argc < 4) {
+               ti->error = "Invalid argument count";
                return -EINVAL;
        }
 
-       fc = kmalloc(sizeof(*fc), GFP_KERNEL);
+       fc = kzalloc(sizeof(*fc), GFP_KERNEL);
        if (!fc) {
-               ti->error = "dm-flakey: Cannot allocate linear context";
+               ti->error = "Cannot allocate linear context";
                return -ENOMEM;
        }
        fc->start_time = jiffies;
 
-       if (sscanf(argv[1], "%llu", &tmp) != 1) {
-               ti->error = "dm-flakey: Invalid device sector";
+       devname = dm_shift_arg(&as);
+
+       if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) {
+               ti->error = "Invalid device sector";
                goto bad;
        }
-       fc->start = tmp;
+       fc->start = tmpll;
 
-       if (sscanf(argv[2], "%u", &fc->up_interval) != 1) {
-               ti->error = "dm-flakey: Invalid up interval";
+       r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
+       if (r)
                goto bad;
-       }
 
-       if (sscanf(argv[3], "%u", &fc->down_interval) != 1) {
-               ti->error = "dm-flakey: Invalid down interval";
+       r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
+       if (r)
                goto bad;
-       }
 
        if (!(fc->up_interval + fc->down_interval)) {
-               ti->error = "dm-flakey: Total (up + down) interval is zero";
+               ti->error = "Total (up + down) interval is zero";
                goto bad;
        }
 
        if (fc->up_interval + fc->down_interval < fc->up_interval) {
-               ti->error = "dm-flakey: Interval overflow";
+               ti->error = "Interval overflow";
                goto bad;
        }
 
-       if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) {
-               ti->error = "dm-flakey: Device lookup failed";
+       r = parse_features(&as, fc, ti);
+       if (r)
+               goto bad;
+
+       if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
+               ti->error = "Device lookup failed";
                goto bad;
        }
 
        ti->num_flush_requests = 1;
+       ti->num_discard_requests = 1;
        ti->private = fc;
        return 0;
 
@@ -99,7 +231,7 @@ static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
 {
        struct flakey_c *fc = ti->private;
 
-       return fc->start + (bi_sector - ti->begin);
+       return fc->start + dm_target_offset(ti, bi_sector);
 }
 
 static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
@@ -111,6 +243,25 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
                bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
 }
 
+static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
+{
+       unsigned bio_bytes = bio_cur_bytes(bio);
+       char *data = bio_data(bio);
+
+       /*
+        * Overwrite the Nth byte of the data returned.
+        */
+       if (data && bio_bytes >= fc->corrupt_bio_byte) {
+               data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
+
+               DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
+                       "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n",
+                       bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
+                       (bio_data_dir(bio) == WRITE) ? 'w' : 'r',
+                       bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes);
+       }
+}
+
 static int flakey_map(struct dm_target *ti, struct bio *bio,
                      union map_info *map_context)
 {
@@ -119,18 +270,71 @@ static int flakey_map(struct dm_target *ti, struct bio *bio,
 
        /* Are we alive ? */
        elapsed = (jiffies - fc->start_time) / HZ;
-       if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval)
+       if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
+               /*
+                * Flag this bio as submitted while down.
+                */
+               map_context->ll = 1;
+
+               /*
+                * Map reads as normal.
+                */
+               if (bio_data_dir(bio) == READ)
+                       goto map_bio;
+
+               /*
+                * Drop writes?
+                */
+               if (test_bit(DROP_WRITES, &fc->flags)) {
+                       bio_endio(bio, 0);
+                       return DM_MAPIO_SUBMITTED;
+               }
+
+               /*
+                * Corrupt matching writes.
+                */
+               if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
+                       if (all_corrupt_bio_flags_match(bio, fc))
+                               corrupt_bio_data(bio, fc);
+                       goto map_bio;
+               }
+
+               /*
+                * By default, error all I/O.
+                */
                return -EIO;
+       }
 
+map_bio:
        flakey_map_bio(ti, bio);
 
        return DM_MAPIO_REMAPPED;
 }
 
+static int flakey_end_io(struct dm_target *ti, struct bio *bio,
+                        int error, union map_info *map_context)
+{
+       struct flakey_c *fc = ti->private;
+       unsigned bio_submitted_while_down = map_context->ll;
+
+       /*
+        * Corrupt successful READs while in down state.
+        * If flags were specified, only corrupt those that match.
+        */
+       if (!error && bio_submitted_while_down &&
+           (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
+           all_corrupt_bio_flags_match(bio, fc))
+               corrupt_bio_data(bio, fc);
+
+       return error;
+}
+
 static int flakey_status(struct dm_target *ti, status_type_t type,
                         char *result, unsigned int maxlen)
 {
+       unsigned sz = 0;
        struct flakey_c *fc = ti->private;
+       unsigned drop_writes;
 
        switch (type) {
        case STATUSTYPE_INFO:
@@ -138,9 +342,22 @@ static int flakey_status(struct dm_target *ti, status_type_t type,
                break;
 
        case STATUSTYPE_TABLE:
-               snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name,
-                        (unsigned long long)fc->start, fc->up_interval,
-                        fc->down_interval);
+               DMEMIT("%s %llu %u %u ", fc->dev->name,
+                      (unsigned long long)fc->start, fc->up_interval,
+                      fc->down_interval);
+
+               drop_writes = test_bit(DROP_WRITES, &fc->flags);
+               DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
+
+               if (drop_writes)
+                       DMEMIT("drop_writes ");
+
+               if (fc->corrupt_bio_byte)
+                       DMEMIT("corrupt_bio_byte %u %c %u %u ",
+                              fc->corrupt_bio_byte,
+                              (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
+                              fc->corrupt_bio_value, fc->corrupt_bio_flags);
+
                break;
        }
        return 0;
@@ -177,11 +394,12 @@ static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_
 
 static struct target_type flakey_target = {
        .name   = "flakey",
-       .version = {1, 1, 0},
+       .version = {1, 2, 0},
        .module = THIS_MODULE,
        .ctr    = flakey_ctr,
        .dtr    = flakey_dtr,
        .map    = flakey_map,
+       .end_io = flakey_end_io,
        .status = flakey_status,
        .ioctl  = flakey_ioctl,
        .merge  = flakey_merge,
index 2067288f61f9b5e868da28c6c7c9694b9a5269cf..ad2eba40e3190e700eab3136b8af5dd150cbf208 100644 (file)
@@ -38,6 +38,8 @@ struct io {
        struct dm_io_client *client;
        io_notify_fn callback;
        void *context;
+       void *vma_invalidate_address;
+       unsigned long vma_invalidate_size;
 } __attribute__((aligned(DM_IO_MAX_REGIONS)));
 
 static struct kmem_cache *_dm_io_cache;
@@ -116,6 +118,10 @@ static void dec_count(struct io *io, unsigned int region, int error)
                set_bit(region, &io->error_bits);
 
        if (atomic_dec_and_test(&io->count)) {
+               if (io->vma_invalidate_size)
+                       invalidate_kernel_vmap_range(io->vma_invalidate_address,
+                                                    io->vma_invalidate_size);
+
                if (io->sleeper)
                        wake_up_process(io->sleeper);
 
@@ -159,6 +165,9 @@ struct dpages {
 
        unsigned context_u;
        void *context_ptr;
+
+       void *vma_invalidate_address;
+       unsigned long vma_invalidate_size;
 };
 
 /*
@@ -377,6 +386,9 @@ static int sync_io(struct dm_io_client *client, unsigned int num_regions,
        io->sleeper = current;
        io->client = client;
 
+       io->vma_invalidate_address = dp->vma_invalidate_address;
+       io->vma_invalidate_size = dp->vma_invalidate_size;
+
        dispatch_io(rw, num_regions, where, dp, io, 1);
 
        while (1) {
@@ -415,13 +427,21 @@ static int async_io(struct dm_io_client *client, unsigned int num_regions,
        io->callback = fn;
        io->context = context;
 
+       io->vma_invalidate_address = dp->vma_invalidate_address;
+       io->vma_invalidate_size = dp->vma_invalidate_size;
+
        dispatch_io(rw, num_regions, where, dp, io, 0);
        return 0;
 }
 
-static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
+static int dp_init(struct dm_io_request *io_req, struct dpages *dp,
+                  unsigned long size)
 {
        /* Set up dpages based on memory type */
+
+       dp->vma_invalidate_address = NULL;
+       dp->vma_invalidate_size = 0;
+
        switch (io_req->mem.type) {
        case DM_IO_PAGE_LIST:
                list_dp_init(dp, io_req->mem.ptr.pl, io_req->mem.offset);
@@ -432,6 +452,11 @@ static int dp_init(struct dm_io_request *io_req, struct dpages *dp)
                break;
 
        case DM_IO_VMA:
+               flush_kernel_vmap_range(io_req->mem.ptr.vma, size);
+               if ((io_req->bi_rw & RW_MASK) == READ) {
+                       dp->vma_invalidate_address = io_req->mem.ptr.vma;
+                       dp->vma_invalidate_size = size;
+               }
                vm_dp_init(dp, io_req->mem.ptr.vma);
                break;
 
@@ -460,7 +485,7 @@ int dm_io(struct dm_io_request *io_req, unsigned num_regions,
        int r;
        struct dpages dp;
 
-       r = dp_init(io_req, &dp);
+       r = dp_init(io_req, &dp, (unsigned long)where->count << SECTOR_SHIFT);
        if (r)
                return r;
 
index 4cacdad2270a345e1ef8455eddf5c2a2838a22e1..2e9a3ca37bdd39c6cbaf36f800b77aa3a04a638f 100644 (file)
@@ -128,6 +128,24 @@ static struct hash_cell *__get_uuid_cell(const char *str)
        return NULL;
 }
 
+static struct hash_cell *__get_dev_cell(uint64_t dev)
+{
+       struct mapped_device *md;
+       struct hash_cell *hc;
+
+       md = dm_get_md(huge_decode_dev(dev));
+       if (!md)
+               return NULL;
+
+       hc = dm_get_mdptr(md);
+       if (!hc) {
+               dm_put(md);
+               return NULL;
+       }
+
+       return hc;
+}
+
 /*-----------------------------------------------------------------
  * Inserting, removing and renaming a device.
  *---------------------------------------------------------------*/
@@ -718,25 +736,45 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
  */
 static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
 {
-       struct mapped_device *md;
-       void *mdptr = NULL;
+       struct hash_cell *hc = NULL;
 
-       if (*param->uuid)
-               return __get_uuid_cell(param->uuid);
+       if (*param->uuid) {
+               if (*param->name || param->dev)
+                       return NULL;
 
-       if (*param->name)
-               return __get_name_cell(param->name);
+               hc = __get_uuid_cell(param->uuid);
+               if (!hc)
+                       return NULL;
+       } else if (*param->name) {
+               if (param->dev)
+                       return NULL;
 
-       md = dm_get_md(huge_decode_dev(param->dev));
-       if (!md)
-               goto out;
+               hc = __get_name_cell(param->name);
+               if (!hc)
+                       return NULL;
+       } else if (param->dev) {
+               hc = __get_dev_cell(param->dev);
+               if (!hc)
+                       return NULL;
+       } else
+               return NULL;
 
-       mdptr = dm_get_mdptr(md);
-       if (!mdptr)
-               dm_put(md);
+       /*
+        * Sneakily write in both the name and the uuid
+        * while we have the cell.
+        */
+       strlcpy(param->name, hc->name, sizeof(param->name));
+       if (hc->uuid)
+               strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
+       else
+               param->uuid[0] = '\0';
 
-out:
-       return mdptr;
+       if (hc->new_map)
+               param->flags |= DM_INACTIVE_PRESENT_FLAG;
+       else
+               param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
+
+       return hc;
 }
 
 static struct mapped_device *find_device(struct dm_ioctl *param)
@@ -746,24 +784,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
 
        down_read(&_hash_lock);
        hc = __find_device_hash_cell(param);
-       if (hc) {
+       if (hc)
                md = hc->md;
-
-               /*
-                * Sneakily write in both the name and the uuid
-                * while we have the cell.
-                */
-               strlcpy(param->name, hc->name, sizeof(param->name));
-               if (hc->uuid)
-                       strlcpy(param->uuid, hc->uuid, sizeof(param->uuid));
-               else
-                       param->uuid[0] = '\0';
-
-               if (hc->new_map)
-                       param->flags |= DM_INACTIVE_PRESENT_FLAG;
-               else
-                       param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
-       }
        up_read(&_hash_lock);
 
        return md;
@@ -1402,6 +1424,11 @@ static int target_message(struct dm_ioctl *param, size_t param_size)
                goto out;
        }
 
+       if (!argc) {
+               DMWARN("Empty message received.");
+               goto out;
+       }
+
        table = dm_get_live_table(md);
        if (!table)
                goto out_argv;
index 320401dec1044215b16bd1c784c1fca2883b3537..f82147029636d87b30e990daa230dfb17ca69c50 100644 (file)
@@ -224,8 +224,6 @@ struct kcopyd_job {
        unsigned int num_dests;
        struct dm_io_region dests[DM_KCOPYD_MAX_REGIONS];
 
-       sector_t offset;
-       unsigned int nr_pages;
        struct page_list *pages;
 
        /*
@@ -380,7 +378,7 @@ static int run_io_job(struct kcopyd_job *job)
                .bi_rw = job->rw,
                .mem.type = DM_IO_PAGE_LIST,
                .mem.ptr.pl = job->pages,
-               .mem.offset = job->offset,
+               .mem.offset = 0,
                .notify.fn = complete_io,
                .notify.context = job,
                .client = job->kc->io_client,
@@ -397,10 +395,9 @@ static int run_io_job(struct kcopyd_job *job)
 static int run_pages_job(struct kcopyd_job *job)
 {
        int r;
+       unsigned nr_pages = dm_div_up(job->dests[0].count, PAGE_SIZE >> 9);
 
-       job->nr_pages = dm_div_up(job->dests[0].count + job->offset,
-                                 PAGE_SIZE >> 9);
-       r = kcopyd_get_pages(job->kc, job->nr_pages, &job->pages);
+       r = kcopyd_get_pages(job->kc, nr_pages, &job->pages);
        if (!r) {
                /* this job is ready for io */
                push(&job->kc->io_jobs, job);
@@ -602,8 +599,6 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
        job->num_dests = num_dests;
        memcpy(&job->dests, dests, sizeof(*dests) * num_dests);
 
-       job->offset = 0;
-       job->nr_pages = 0;
        job->pages = NULL;
 
        job->fn = fn;
@@ -622,6 +617,37 @@ int dm_kcopyd_copy(struct dm_kcopyd_client *kc, struct dm_io_region *from,
 }
 EXPORT_SYMBOL(dm_kcopyd_copy);
 
+void *dm_kcopyd_prepare_callback(struct dm_kcopyd_client *kc,
+                                dm_kcopyd_notify_fn fn, void *context)
+{
+       struct kcopyd_job *job;
+
+       job = mempool_alloc(kc->job_pool, GFP_NOIO);
+
+       memset(job, 0, sizeof(struct kcopyd_job));
+       job->kc = kc;
+       job->fn = fn;
+       job->context = context;
+
+       atomic_inc(&kc->nr_jobs);
+
+       return job;
+}
+EXPORT_SYMBOL(dm_kcopyd_prepare_callback);
+
+void dm_kcopyd_do_callback(void *j, int read_err, unsigned long write_err)
+{
+       struct kcopyd_job *job = j;
+       struct dm_kcopyd_client *kc = job->kc;
+
+       job->read_err = read_err;
+       job->write_err = write_err;
+
+       push(&kc->complete_jobs, job);
+       wake(kc);
+}
+EXPORT_SYMBOL(dm_kcopyd_do_callback);
+
 /*
  * Cancels a kcopyd job, eg. someone might be deactivating a
  * mirror.
index aa2e0c374ab3e0c985e6bdc6536807bc07bcf3ce..1021c89860116a5bb6e3a2faea1362c0ea805f52 100644 (file)
@@ -394,8 +394,7 @@ static int flush_by_group(struct log_c *lc, struct list_head *flush_list)
                        group[count] = fe->region;
                        count++;
 
-                       list_del(&fe->list);
-                       list_add(&fe->list, &tmp_list);
+                       list_move(&fe->list, &tmp_list);
 
                        type = fe->type;
                        if (count >= MAX_FLUSH_GROUP_COUNT)
index 948e3f4925bfe6d28a39aeed22e11324f1b1b8df..3b52bb72bd1f0cb8717798c121008a76784b9aa0 100644 (file)
@@ -197,15 +197,21 @@ EXPORT_SYMBOL(dm_dirty_log_destroy);
 #define MIRROR_DISK_VERSION 2
 #define LOG_OFFSET 2
 
-struct log_header {
-       uint32_t magic;
+struct log_header_disk {
+       __le32 magic;
 
        /*
         * Simple, incrementing version. no backward
         * compatibility.
         */
+       __le32 version;
+       __le64 nr_regions;
+} __packed;
+
+struct log_header_core {
+       uint32_t magic;
        uint32_t version;
-       sector_t nr_regions;
+       uint64_t nr_regions;
 };
 
 struct log_c {
@@ -239,10 +245,10 @@ struct log_c {
        int log_dev_failed;
        int log_dev_flush_failed;
        struct dm_dev *log_dev;
-       struct log_header header;
+       struct log_header_core header;
 
        struct dm_io_region header_location;
-       struct log_header *disk_header;
+       struct log_header_disk *disk_header;
 };
 
 /*
@@ -251,34 +257,34 @@ struct log_c {
  */
 static inline int log_test_bit(uint32_t *bs, unsigned bit)
 {
-       return test_bit_le(bit, (unsigned long *) bs) ? 1 : 0;
+       return test_bit_le(bit, bs) ? 1 : 0;
 }
 
 static inline void log_set_bit(struct log_c *l,
                               uint32_t *bs, unsigned bit)
 {
-       __test_and_set_bit_le(bit, (unsigned long *) bs);
+       __set_bit_le(bit, bs);
        l->touched_cleaned = 1;
 }
 
 static inline void log_clear_bit(struct log_c *l,
                                 uint32_t *bs, unsigned bit)
 {
-       __test_and_clear_bit_le(bit, (unsigned long *) bs);
+       __clear_bit_le(bit, bs);
        l->touched_dirtied = 1;
 }
 
 /*----------------------------------------------------------------
  * Header IO
  *--------------------------------------------------------------*/
-static void header_to_disk(struct log_header *core, struct log_header *disk)
+static void header_to_disk(struct log_header_core *core, struct log_header_disk *disk)
 {
        disk->magic = cpu_to_le32(core->magic);
        disk->version = cpu_to_le32(core->version);
        disk->nr_regions = cpu_to_le64(core->nr_regions);
 }
 
-static void header_from_disk(struct log_header *core, struct log_header *disk)
+static void header_from_disk(struct log_header_core *core, struct log_header_disk *disk)
 {
        core->magic = le32_to_cpu(disk->magic);
        core->version = le32_to_cpu(disk->version);
@@ -486,7 +492,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
        memset(lc->sync_bits, (sync == NOSYNC) ? -1 : 0, bitset_size);
        lc->sync_count = (sync == NOSYNC) ? region_count : 0;
 
-       lc->recovering_bits = vmalloc(bitset_size);
+       lc->recovering_bits = vzalloc(bitset_size);
        if (!lc->recovering_bits) {
                DMWARN("couldn't allocate sync bitset");
                vfree(lc->sync_bits);
@@ -498,7 +504,6 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
                kfree(lc);
                return -ENOMEM;
        }
-       memset(lc->recovering_bits, 0, bitset_size);
        lc->sync_search = 0;
        log->context = lc;
 
@@ -739,8 +744,7 @@ static int core_get_resync_work(struct dm_dirty_log *log, region_t *region)
                return 0;
 
        do {
-               *region = find_next_zero_bit_le(
-                                            (unsigned long *) lc->sync_bits,
+               *region = find_next_zero_bit_le(lc->sync_bits,
                                             lc->region_count,
                                             lc->sync_search);
                lc->sync_search = *region + 1;
index c3547016f0f1e7156d1b28b0fded62b5bf534008..5e0090ef4182e71de26afec04d23edb19b3f4e39 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/atomic.h>
 
 #define DM_MSG_PREFIX "multipath"
-#define MESG_STR(x) x, sizeof(x)
 #define DM_PG_INIT_DELAY_MSECS 2000
 #define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
 
@@ -505,80 +504,29 @@ static void trigger_event(struct work_struct *work)
  *      <#paths> <#per-path selector args>
  *         [<path> [<arg>]* ]+ ]+
  *---------------------------------------------------------------*/
-struct param {
-       unsigned min;
-       unsigned max;
-       char *error;
-};
-
-static int read_param(struct param *param, char *str, unsigned *v, char **error)
-{
-       if (!str ||
-           (sscanf(str, "%u", v) != 1) ||
-           (*v < param->min) ||
-           (*v > param->max)) {
-               *error = param->error;
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-struct arg_set {
-       unsigned argc;
-       char **argv;
-};
-
-static char *shift(struct arg_set *as)
-{
-       char *r;
-
-       if (as->argc) {
-               as->argc--;
-               r = *as->argv;
-               as->argv++;
-               return r;
-       }
-
-       return NULL;
-}
-
-static void consume(struct arg_set *as, unsigned n)
-{
-       BUG_ON (as->argc < n);
-       as->argc -= n;
-       as->argv += n;
-}
-
-static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
+static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
                               struct dm_target *ti)
 {
        int r;
        struct path_selector_type *pst;
        unsigned ps_argc;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of path selector args"},
        };
 
-       pst = dm_get_path_selector(shift(as));
+       pst = dm_get_path_selector(dm_shift_arg(as));
        if (!pst) {
                ti->error = "unknown path selector type";
                return -EINVAL;
        }
 
-       r = read_param(_params, shift(as), &ps_argc, &ti->error);
+       r = dm_read_arg_group(_args, as, &ps_argc, &ti->error);
        if (r) {
                dm_put_path_selector(pst);
                return -EINVAL;
        }
 
-       if (ps_argc > as->argc) {
-               dm_put_path_selector(pst);
-               ti->error = "not enough arguments for path selector";
-               return -EINVAL;
-       }
-
        r = pst->create(&pg->ps, ps_argc, as->argv);
        if (r) {
                dm_put_path_selector(pst);
@@ -587,12 +535,12 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
        }
 
        pg->ps.type = pst;
-       consume(as, ps_argc);
+       dm_consume_args(as, ps_argc);
 
        return 0;
 }
 
-static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
+static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
                               struct dm_target *ti)
 {
        int r;
@@ -609,7 +557,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
        if (!p)
                return ERR_PTR(-ENOMEM);
 
-       r = dm_get_device(ti, shift(as), dm_table_get_mode(ti->table),
+       r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
                          &p->path.dev);
        if (r) {
                ti->error = "error getting device";
@@ -660,16 +608,16 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
        return ERR_PTR(r);
 }
 
-static struct priority_group *parse_priority_group(struct arg_set *as,
+static struct priority_group *parse_priority_group(struct dm_arg_set *as,
                                                   struct multipath *m)
 {
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {1, 1024, "invalid number of paths"},
                {0, 1024, "invalid number of selector args"}
        };
 
        int r;
-       unsigned i, nr_selector_args, nr_params;
+       unsigned i, nr_selector_args, nr_args;
        struct priority_group *pg;
        struct dm_target *ti = m->ti;
 
@@ -693,26 +641,26 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
        /*
         * read the paths
         */
-       r = read_param(_params, shift(as), &pg->nr_pgpaths, &ti->error);
+       r = dm_read_arg(_args, as, &pg->nr_pgpaths, &ti->error);
        if (r)
                goto bad;
 
-       r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+       r = dm_read_arg(_args + 1, as, &nr_selector_args, &ti->error);
        if (r)
                goto bad;
 
-       nr_params = 1 + nr_selector_args;
+       nr_args = 1 + nr_selector_args;
        for (i = 0; i < pg->nr_pgpaths; i++) {
                struct pgpath *pgpath;
-               struct arg_set path_args;
+               struct dm_arg_set path_args;
 
-               if (as->argc < nr_params) {
+               if (as->argc < nr_args) {
                        ti->error = "not enough path parameters";
                        r = -EINVAL;
                        goto bad;
                }
 
-               path_args.argc = nr_params;
+               path_args.argc = nr_args;
                path_args.argv = as->argv;
 
                pgpath = parse_path(&path_args, &pg->ps, ti);
@@ -723,7 +671,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
 
                pgpath->pg = pg;
                list_add_tail(&pgpath->list, &pg->pgpaths);
-               consume(as, nr_params);
+               dm_consume_args(as, nr_args);
        }
 
        return pg;
@@ -733,28 +681,23 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
        return ERR_PTR(r);
 }
 
-static int parse_hw_handler(struct arg_set *as, struct multipath *m)
+static int parse_hw_handler(struct dm_arg_set *as, struct multipath *m)
 {
        unsigned hw_argc;
        int ret;
        struct dm_target *ti = m->ti;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of hardware handler args"},
        };
 
-       if (read_param(_params, shift(as), &hw_argc, &ti->error))
+       if (dm_read_arg_group(_args, as, &hw_argc, &ti->error))
                return -EINVAL;
 
        if (!hw_argc)
                return 0;
 
-       if (hw_argc > as->argc) {
-               ti->error = "not enough arguments for hardware handler";
-               return -EINVAL;
-       }
-
-       m->hw_handler_name = kstrdup(shift(as), GFP_KERNEL);
+       m->hw_handler_name = kstrdup(dm_shift_arg(as), GFP_KERNEL);
        request_module("scsi_dh_%s", m->hw_handler_name);
        if (scsi_dh_handler_exist(m->hw_handler_name) == 0) {
                ti->error = "unknown hardware handler type";
@@ -778,7 +721,7 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m)
                for (i = 0, p+=j+1; i <= hw_argc - 2; i++, p+=j+1)
                        j = sprintf(p, "%s", as->argv[i]);
        }
-       consume(as, hw_argc - 1);
+       dm_consume_args(as, hw_argc - 1);
 
        return 0;
 fail:
@@ -787,20 +730,20 @@ fail:
        return ret;
 }
 
-static int parse_features(struct arg_set *as, struct multipath *m)
+static int parse_features(struct dm_arg_set *as, struct multipath *m)
 {
        int r;
        unsigned argc;
        struct dm_target *ti = m->ti;
-       const char *param_name;
+       const char *arg_name;
 
-       static struct param _params[] = {
+       static struct dm_arg _args[] = {
                {0, 5, "invalid number of feature args"},
                {1, 50, "pg_init_retries must be between 1 and 50"},
                {0, 60000, "pg_init_delay_msecs must be between 0 and 60000"},
        };
 
-       r = read_param(_params, shift(as), &argc, &ti->error);
+       r = dm_read_arg_group(_args, as, &argc, &ti->error);
        if (r)
                return -EINVAL;
 
@@ -808,26 +751,24 @@ static int parse_features(struct arg_set *as, struct multipath *m)
                return 0;
 
        do {
-               param_name = shift(as);
+               arg_name = dm_shift_arg(as);
                argc--;
 
-               if (!strnicmp(param_name, MESG_STR("queue_if_no_path"))) {
+               if (!strcasecmp(arg_name, "queue_if_no_path")) {
                        r = queue_if_no_path(m, 1, 0);
                        continue;
                }
 
-               if (!strnicmp(param_name, MESG_STR("pg_init_retries")) &&
+               if (!strcasecmp(arg_name, "pg_init_retries") &&
                    (argc >= 1)) {
-                       r = read_param(_params + 1, shift(as),
-                                      &m->pg_init_retries, &ti->error);
+                       r = dm_read_arg(_args + 1, as, &m->pg_init_retries, &ti->error);
                        argc--;
                        continue;
                }
 
-               if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) &&
+               if (!strcasecmp(arg_name, "pg_init_delay_msecs") &&
                    (argc >= 1)) {
-                       r = read_param(_params + 2, shift(as),
-                                      &m->pg_init_delay_msecs, &ti->error);
+                       r = dm_read_arg(_args + 2, as, &m->pg_init_delay_msecs, &ti->error);
                        argc--;
                        continue;
                }
@@ -842,15 +783,15 @@ static int parse_features(struct arg_set *as, struct multipath *m)
 static int multipath_ctr(struct dm_target *ti, unsigned int argc,
                         char **argv)
 {
-       /* target parameters */
-       static struct param _params[] = {
+       /* target arguments */
+       static struct dm_arg _args[] = {
                {0, 1024, "invalid number of priority groups"},
                {0, 1024, "invalid initial priority group number"},
        };
 
        int r;
        struct multipath *m;
-       struct arg_set as;
+       struct dm_arg_set as;
        unsigned pg_count = 0;
        unsigned next_pg_num;
 
@@ -871,11 +812,11 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
        if (r)
                goto bad;
 
-       r = read_param(_params, shift(&as), &m->nr_priority_groups, &ti->error);
+       r = dm_read_arg(_args, &as, &m->nr_priority_groups, &ti->error);
        if (r)
                goto bad;
 
-       r = read_param(_params + 1, shift(&as), &next_pg_num, &ti->error);
+       r = dm_read_arg(_args + 1, &as, &next_pg_num, &ti->error);
        if (r)
                goto bad;
 
@@ -1505,10 +1446,10 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
        }
 
        if (argc == 1) {
-               if (!strnicmp(argv[0], MESG_STR("queue_if_no_path"))) {
+               if (!strcasecmp(argv[0], "queue_if_no_path")) {
                        r = queue_if_no_path(m, 1, 0);
                        goto out;
-               } else if (!strnicmp(argv[0], MESG_STR("fail_if_no_path"))) {
+               } else if (!strcasecmp(argv[0], "fail_if_no_path")) {
                        r = queue_if_no_path(m, 0, 0);
                        goto out;
                }
@@ -1519,18 +1460,18 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
                goto out;
        }
 
-       if (!strnicmp(argv[0], MESG_STR("disable_group"))) {
+       if (!strcasecmp(argv[0], "disable_group")) {
                r = bypass_pg_num(m, argv[1], 1);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("enable_group"))) {
+       } else if (!strcasecmp(argv[0], "enable_group")) {
                r = bypass_pg_num(m, argv[1], 0);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("switch_group"))) {
+       } else if (!strcasecmp(argv[0], "switch_group")) {
                r = switch_pg_num(m, argv[1]);
                goto out;
-       } else if (!strnicmp(argv[0], MESG_STR("reinstate_path")))
+       } else if (!strcasecmp(argv[0], "reinstate_path"))
                action = reinstate_path;
-       else if (!strnicmp(argv[0], MESG_STR("fail_path")))
+       else if (!strcasecmp(argv[0], "fail_path"))
                action = fail_path;
        else {
                DMWARN("Unrecognised multipath message received.");
index e5d8904fc8f647162d4a7d79150491797cfda6d2..a002dd85db1e674e2efbc188a531f001c2d0716b 100644 (file)
@@ -8,19 +8,19 @@
 #include <linux/slab.h>
 
 #include "md.h"
+#include "raid1.h"
 #include "raid5.h"
-#include "dm.h"
 #include "bitmap.h"
 
+#include <linux/device-mapper.h>
+
 #define DM_MSG_PREFIX "raid"
 
 /*
- * If the MD doesn't support MD_SYNC_STATE_FORCED yet, then
- * make it so the flag doesn't set anything.
+ * The following flags are used by dm-raid.c to set up the array state.
+ * They must be cleared before md_run is called.
  */
-#ifndef MD_SYNC_STATE_FORCED
-#define MD_SYNC_STATE_FORCED 0
-#endif
+#define FirstUse 10             /* rdev flag */
 
 struct raid_dev {
        /*
@@ -43,14 +43,15 @@ struct raid_dev {
 /*
  * Flags for rs->print_flags field.
  */
-#define DMPF_DAEMON_SLEEP      0x1
-#define DMPF_MAX_WRITE_BEHIND  0x2
-#define DMPF_SYNC              0x4
-#define DMPF_NOSYNC            0x8
-#define DMPF_STRIPE_CACHE      0x10
-#define DMPF_MIN_RECOVERY_RATE 0x20
-#define DMPF_MAX_RECOVERY_RATE 0x40
-
+#define DMPF_SYNC              0x1
+#define DMPF_NOSYNC            0x2
+#define DMPF_REBUILD           0x4
+#define DMPF_DAEMON_SLEEP      0x8
+#define DMPF_MIN_RECOVERY_RATE 0x10
+#define DMPF_MAX_RECOVERY_RATE 0x20
+#define DMPF_MAX_WRITE_BEHIND  0x40
+#define DMPF_STRIPE_CACHE      0x80
+#define DMPF_REGION_SIZE       0X100
 struct raid_set {
        struct dm_target *ti;
 
@@ -72,6 +73,7 @@ static struct raid_type {
        const unsigned level;           /* RAID level. */
        const unsigned algorithm;       /* RAID algorithm. */
 } raid_types[] = {
+       {"raid1",    "RAID1 (mirroring)",               0, 2, 1, 0 /* NONE */},
        {"raid4",    "RAID4 (dedicated parity disk)",   1, 2, 5, ALGORITHM_PARITY_0},
        {"raid5_la", "RAID5 (left asymmetric)",         1, 2, 5, ALGORITHM_LEFT_ASYMMETRIC},
        {"raid5_ra", "RAID5 (right asymmetric)",        1, 2, 5, ALGORITHM_RIGHT_ASYMMETRIC},
@@ -105,7 +107,8 @@ static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *ra
        }
 
        sectors_per_dev = ti->len;
-       if (sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
+       if ((raid_type->level > 1) &&
+           sector_div(sectors_per_dev, (raid_devs - raid_type->parity_devs))) {
                ti->error = "Target length not divisible by number of data devices";
                return ERR_PTR(-EINVAL);
        }
@@ -147,9 +150,16 @@ static void context_free(struct raid_set *rs)
 {
        int i;
 
-       for (i = 0; i < rs->md.raid_disks; i++)
+       for (i = 0; i < rs->md.raid_disks; i++) {
+               if (rs->dev[i].meta_dev)
+                       dm_put_device(rs->ti, rs->dev[i].meta_dev);
+               if (rs->dev[i].rdev.sb_page)
+                       put_page(rs->dev[i].rdev.sb_page);
+               rs->dev[i].rdev.sb_page = NULL;
+               rs->dev[i].rdev.sb_loaded = 0;
                if (rs->dev[i].data_dev)
                        dm_put_device(rs->ti, rs->dev[i].data_dev);
+       }
 
        kfree(rs);
 }
@@ -159,7 +169,16 @@ static void context_free(struct raid_set *rs)
  *  <meta_dev>: meta device name or '-' if missing
  *  <data_dev>: data device name or '-' if missing
  *
- * This code parses those words.
+ * The following are permitted:
+ *    - -
+ *    - <data_dev>
+ *    <meta_dev> <data_dev>
+ *
+ * The following is not allowed:
+ *    <meta_dev> -
+ *
+ * This code parses those words.  If there is a failure,
+ * the caller must use context_free to unwind the operations.
  */
 static int dev_parms(struct raid_set *rs, char **argv)
 {
@@ -182,8 +201,16 @@ static int dev_parms(struct raid_set *rs, char **argv)
                rs->dev[i].rdev.mddev = &rs->md;
 
                if (strcmp(argv[0], "-")) {
-                       rs->ti->error = "Metadata devices not supported";
-                       return -EINVAL;
+                       ret = dm_get_device(rs->ti, argv[0],
+                                           dm_table_get_mode(rs->ti->table),
+                                           &rs->dev[i].meta_dev);
+                       rs->ti->error = "RAID metadata device lookup failure";
+                       if (ret)
+                               return ret;
+
+                       rs->dev[i].rdev.sb_page = alloc_page(GFP_KERNEL);
+                       if (!rs->dev[i].rdev.sb_page)
+                               return -ENOMEM;
                }
 
                if (!strcmp(argv[1], "-")) {
@@ -193,6 +220,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
                                return -EINVAL;
                        }
 
+                       rs->ti->error = "No data device supplied with metadata device";
+                       if (rs->dev[i].meta_dev)
+                               return -EINVAL;
+
                        continue;
                }
 
@@ -204,6 +235,10 @@ static int dev_parms(struct raid_set *rs, char **argv)
                        return ret;
                }
 
+               if (rs->dev[i].meta_dev) {
+                       metadata_available = 1;
+                       rs->dev[i].rdev.meta_bdev = rs->dev[i].meta_dev->bdev;
+               }
                rs->dev[i].rdev.bdev = rs->dev[i].data_dev->bdev;
                list_add(&rs->dev[i].rdev.same_set, &rs->md.disks);
                if (!test_bit(In_sync, &rs->dev[i].rdev.flags))
@@ -234,34 +269,110 @@ static int dev_parms(struct raid_set *rs, char **argv)
        return 0;
 }
 
+/*
+ * validate_region_size
+ * @rs
+ * @region_size:  region size in sectors.  If 0, pick a size (4MiB default).
+ *
+ * Set rs->md.bitmap_info.chunksize (which really refers to 'region size').
+ * Ensure that (ti->len/region_size < 2^21) - required by MD bitmap.
+ *
+ * Returns: 0 on success, -EINVAL on failure.
+ */
+static int validate_region_size(struct raid_set *rs, unsigned long region_size)
+{
+       unsigned long min_region_size = rs->ti->len / (1 << 21);
+
+       if (!region_size) {
+               /*
+                * Choose a reasonable default.  All figures in sectors.
+                */
+               if (min_region_size > (1 << 13)) {
+                       DMINFO("Choosing default region size of %lu sectors",
+                              region_size);
+                       region_size = min_region_size;
+               } else {
+                       DMINFO("Choosing default region size of 4MiB");
+                       region_size = 1 << 13; /* sectors */
+               }
+       } else {
+               /*
+                * Validate user-supplied value.
+                */
+               if (region_size > rs->ti->len) {
+                       rs->ti->error = "Supplied region size is too large";
+                       return -EINVAL;
+               }
+
+               if (region_size < min_region_size) {
+                       DMERR("Supplied region_size (%lu sectors) below minimum (%lu)",
+                             region_size, min_region_size);
+                       rs->ti->error = "Supplied region size is too small";
+                       return -EINVAL;
+               }
+
+               if (!is_power_of_2(region_size)) {
+                       rs->ti->error = "Region size is not a power of 2";
+                       return -EINVAL;
+               }
+
+               if (region_size < rs->md.chunk_sectors) {
+                       rs->ti->error = "Region size is smaller than the chunk size";
+                       return -EINVAL;
+               }
+       }
+
+       /*
+        * Convert sectors to bytes.
+        */
+       rs->md.bitmap_info.chunksize = (region_size << 9);
+
+       return 0;
+}
+
 /*
  * Possible arguments are...
- * RAID456:
  *     <chunk_size> [optional_args]
  *
- * Optional args:
- *    [[no]sync]                       Force or prevent recovery of the entire array
+ * Argument definitions
+ *    <chunk_size>                     The number of sectors per disk that
+ *                                      will form the "stripe"
+ *    [[no]sync]                       Force or prevent recovery of the
+ *                                      entire array
  *    [rebuild <idx>]                  Rebuild the drive indicated by the index
- *    [daemon_sleep <ms>]              Time between bitmap daemon work to clear bits
+ *    [daemon_sleep <ms>]              Time between bitmap daemon work to
+ *                                      clear bits
  *    [min_recovery_rate <kB/sec/disk>]        Throttle RAID initialization
  *    [max_recovery_rate <kB/sec/disk>]        Throttle RAID initialization
+ *    [write_mostly <idx>]             Indicate a write mostly drive via index
  *    [max_write_behind <sectors>]     See '-write-behind=' (man mdadm)
  *    [stripe_cache <sectors>]         Stripe cache size for higher RAIDs
+ *    [region_size <sectors>]           Defines granularity of bitmap
  */
 static int parse_raid_params(struct raid_set *rs, char **argv,
                             unsigned num_raid_params)
 {
        unsigned i, rebuild_cnt = 0;
-       unsigned long value;
+       unsigned long value, region_size = 0;
        char *key;
 
        /*
         * First, parse the in-order required arguments
+        * "chunk_size" is the only argument of this type.
         */
-       if ((strict_strtoul(argv[0], 10, &value) < 0) ||
-           !is_power_of_2(value) || (value < 8)) {
+       if ((strict_strtoul(argv[0], 10, &value) < 0)) {
                rs->ti->error = "Bad chunk size";
                return -EINVAL;
+       } else if (rs->raid_type->level == 1) {
+               if (value)
+                       DMERR("Ignoring chunk size parameter for RAID 1");
+               value = 0;
+       } else if (!is_power_of_2(value)) {
+               rs->ti->error = "Chunk size must be a power of 2";
+               return -EINVAL;
+       } else if (value < 8) {
+               rs->ti->error = "Chunk size value is too small";
+               return -EINVAL;
        }
 
        rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;
@@ -269,22 +380,39 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
        num_raid_params--;
 
        /*
-        * Second, parse the unordered optional arguments
+        * We set each individual device as In_sync with a completed
+        * 'recovery_offset'.  If there has been a device failure or
+        * replacement then one of the following cases applies:
+        *
+        *   1) User specifies 'rebuild'.
+        *      - Device is reset when param is read.
+        *   2) A new device is supplied.
+        *      - No matching superblock found, resets device.
+        *   3) Device failure was transient and returns on reload.
+        *      - Failure noticed, resets device for bitmap replay.
+        *   4) Device hadn't completed recovery after previous failure.
+        *      - Superblock is read and overrides recovery_offset.
+        *
+        * What is found in the superblocks of the devices is always
+        * authoritative, unless 'rebuild' or '[no]sync' was specified.
         */
-       for (i = 0; i < rs->md.raid_disks; i++)
+       for (i = 0; i < rs->md.raid_disks; i++) {
                set_bit(In_sync, &rs->dev[i].rdev.flags);
+               rs->dev[i].rdev.recovery_offset = MaxSector;
+       }
 
+       /*
+        * Second, parse the unordered optional arguments
+        */
        for (i = 0; i < num_raid_params; i++) {
-               if (!strcmp(argv[i], "nosync")) {
+               if (!strcasecmp(argv[i], "nosync")) {
                        rs->md.recovery_cp = MaxSector;
                        rs->print_flags |= DMPF_NOSYNC;
-                       rs->md.flags |= MD_SYNC_STATE_FORCED;
                        continue;
                }
-               if (!strcmp(argv[i], "sync")) {
+               if (!strcasecmp(argv[i], "sync")) {
                        rs->md.recovery_cp = 0;
                        rs->print_flags |= DMPF_SYNC;
-                       rs->md.flags |= MD_SYNC_STATE_FORCED;
                        continue;
                }
 
@@ -300,9 +428,13 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                        return -EINVAL;
                }
 
-               if (!strcmp(key, "rebuild")) {
-                       if (++rebuild_cnt > rs->raid_type->parity_devs) {
-                               rs->ti->error = "Too many rebuild drives given";
+               if (!strcasecmp(key, "rebuild")) {
+                       rebuild_cnt++;
+                       if (((rs->raid_type->level != 1) &&
+                            (rebuild_cnt > rs->raid_type->parity_devs)) ||
+                           ((rs->raid_type->level == 1) &&
+                            (rebuild_cnt > (rs->md.raid_disks - 1)))) {
+                               rs->ti->error = "Too many rebuild devices specified for given RAID type";
                                return -EINVAL;
                        }
                        if (value > rs->md.raid_disks) {
@@ -311,7 +443,22 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                        }
                        clear_bit(In_sync, &rs->dev[value].rdev.flags);
                        rs->dev[value].rdev.recovery_offset = 0;
-               } else if (!strcmp(key, "max_write_behind")) {
+                       rs->print_flags |= DMPF_REBUILD;
+               } else if (!strcasecmp(key, "write_mostly")) {
+                       if (rs->raid_type->level != 1) {
+                               rs->ti->error = "write_mostly option is only valid for RAID1";
+                               return -EINVAL;
+                       }
+                       if (value > rs->md.raid_disks) {
+                               rs->ti->error = "Invalid write_mostly drive index given";
+                               return -EINVAL;
+                       }
+                       set_bit(WriteMostly, &rs->dev[value].rdev.flags);
+               } else if (!strcasecmp(key, "max_write_behind")) {
+                       if (rs->raid_type->level != 1) {
+                               rs->ti->error = "max_write_behind option is only valid for RAID1";
+                               return -EINVAL;
+                       }
                        rs->print_flags |= DMPF_MAX_WRITE_BEHIND;
 
                        /*
@@ -324,14 +471,14 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                                return -EINVAL;
                        }
                        rs->md.bitmap_info.max_write_behind = value;
-               } else if (!strcmp(key, "daemon_sleep")) {
+               } else if (!strcasecmp(key, "daemon_sleep")) {
                        rs->print_flags |= DMPF_DAEMON_SLEEP;
                        if (!value || (value > MAX_SCHEDULE_TIMEOUT)) {
                                rs->ti->error = "daemon sleep period out of range";
                                return -EINVAL;
                        }
                        rs->md.bitmap_info.daemon_sleep = value;
-               } else if (!strcmp(key, "stripe_cache")) {
+               } else if (!strcasecmp(key, "stripe_cache")) {
                        rs->print_flags |= DMPF_STRIPE_CACHE;
 
                        /*
@@ -348,20 +495,23 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                                rs->ti->error = "Bad stripe_cache size";
                                return -EINVAL;
                        }
-               } else if (!strcmp(key, "min_recovery_rate")) {
+               } else if (!strcasecmp(key, "min_recovery_rate")) {
                        rs->print_flags |= DMPF_MIN_RECOVERY_RATE;
                        if (value > INT_MAX) {
                                rs->ti->error = "min_recovery_rate out of range";
                                return -EINVAL;
                        }
                        rs->md.sync_speed_min = (int)value;
-               } else if (!strcmp(key, "max_recovery_rate")) {
+               } else if (!strcasecmp(key, "max_recovery_rate")) {
                        rs->print_flags |= DMPF_MAX_RECOVERY_RATE;
                        if (value > INT_MAX) {
                                rs->ti->error = "max_recovery_rate out of range";
                                return -EINVAL;
                        }
                        rs->md.sync_speed_max = (int)value;
+               } else if (!strcasecmp(key, "region_size")) {
+                       rs->print_flags |= DMPF_REGION_SIZE;
+                       region_size = value;
                } else {
                        DMERR("Unable to parse RAID parameter: %s", key);
                        rs->ti->error = "Unable to parse RAID parameters";
@@ -369,6 +519,19 @@ static int parse_raid_params(struct raid_set *rs, char **argv,
                }
        }
 
+       if (validate_region_size(rs, region_size))
+               return -EINVAL;
+
+       if (rs->md.chunk_sectors)
+               rs->ti->split_io = rs->md.chunk_sectors;
+       else
+               rs->ti->split_io = region_size;
+
+       if (rs->md.chunk_sectors)
+               rs->ti->split_io = rs->md.chunk_sectors;
+       else
+               rs->ti->split_io = region_size;
+
        /* Assume there are no metadata devices until the drives are parsed */
        rs->md.persistent = 0;
        rs->md.external = 1;
@@ -387,17 +550,351 @@ static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
 {
        struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
 
+       if (rs->raid_type->level == 1)
+               return md_raid1_congested(&rs->md, bits);
+
        return md_raid5_congested(&rs->md, bits);
 }
 
+/*
+ * This structure is never routinely used by userspace, unlike md superblocks.
+ * Devices with this superblock should only ever be accessed via device-mapper.
+ */
+#define DM_RAID_MAGIC 0x64526D44
+struct dm_raid_superblock {
+       __le32 magic;           /* "DmRd" */
+       __le32 features;        /* Used to indicate possible future changes */
+
+       __le32 num_devices;     /* Number of devices in this array. (Max 64) */
+       __le32 array_position;  /* The position of this drive in the array */
+
+       __le64 events;          /* Incremented by md when superblock updated */
+       __le64 failed_devices;  /* Bit field of devices to indicate failures */
+
+       /*
+        * This offset tracks the progress of the repair or replacement of
+        * an individual drive.
+        */
+       __le64 disk_recovery_offset;
+
+       /*
+        * This offset tracks the progress of the initial array
+        * synchronisation/parity calculation.
+        */
+       __le64 array_resync_offset;
+
+       /*
+        * RAID characteristics
+        */
+       __le32 level;
+       __le32 layout;
+       __le32 stripe_sectors;
+
+       __u8 pad[452];          /* Round struct to 512 bytes. */
+                               /* Always set to 0 when writing. */
+} __packed;
+
+static int read_disk_sb(mdk_rdev_t *rdev, int size)
+{
+       BUG_ON(!rdev->sb_page);
+
+       if (rdev->sb_loaded)
+               return 0;
+
+       if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, 1)) {
+               DMERR("Failed to read device superblock");
+               return -EINVAL;
+       }
+
+       rdev->sb_loaded = 1;
+
+       return 0;
+}
+
+static void super_sync(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       mdk_rdev_t *r, *t;
+       uint64_t failed_devices;
+       struct dm_raid_superblock *sb;
+
+       sb = page_address(rdev->sb_page);
+       failed_devices = le64_to_cpu(sb->failed_devices);
+
+       rdev_for_each(r, t, mddev)
+               if ((r->raid_disk >= 0) && test_bit(Faulty, &r->flags))
+                       failed_devices |= (1ULL << r->raid_disk);
+
+       memset(sb, 0, sizeof(*sb));
+
+       sb->magic = cpu_to_le32(DM_RAID_MAGIC);
+       sb->features = cpu_to_le32(0);  /* No features yet */
+
+       sb->num_devices = cpu_to_le32(mddev->raid_disks);
+       sb->array_position = cpu_to_le32(rdev->raid_disk);
+
+       sb->events = cpu_to_le64(mddev->events);
+       sb->failed_devices = cpu_to_le64(failed_devices);
+
+       sb->disk_recovery_offset = cpu_to_le64(rdev->recovery_offset);
+       sb->array_resync_offset = cpu_to_le64(mddev->recovery_cp);
+
+       sb->level = cpu_to_le32(mddev->level);
+       sb->layout = cpu_to_le32(mddev->layout);
+       sb->stripe_sectors = cpu_to_le32(mddev->chunk_sectors);
+}
+
+/*
+ * super_load
+ *
+ * This function creates a superblock if one is not found on the device
+ * and will decide which superblock to use if there's a choice.
+ *
+ * Return: 1 if use rdev, 0 if use refdev, -Exxx otherwise
+ */
+static int super_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev)
+{
+       int ret;
+       struct dm_raid_superblock *sb;
+       struct dm_raid_superblock *refsb;
+       uint64_t events_sb, events_refsb;
+
+       rdev->sb_start = 0;
+       rdev->sb_size = sizeof(*sb);
+
+       ret = read_disk_sb(rdev, rdev->sb_size);
+       if (ret)
+               return ret;
+
+       sb = page_address(rdev->sb_page);
+       if (sb->magic != cpu_to_le32(DM_RAID_MAGIC)) {
+               super_sync(rdev->mddev, rdev);
+
+               set_bit(FirstUse, &rdev->flags);
+
+               /* Force writing of superblocks to disk */
+               set_bit(MD_CHANGE_DEVS, &rdev->mddev->flags);
+
+               /* Any superblock is better than none, choose that if given */
+               return refdev ? 0 : 1;
+       }
+
+       if (!refdev)
+               return 1;
+
+       events_sb = le64_to_cpu(sb->events);
+
+       refsb = page_address(refdev->sb_page);
+       events_refsb = le64_to_cpu(refsb->events);
+
+       return (events_sb > events_refsb) ? 1 : 0;
+}
+
+static int super_init_validation(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       int role;
+       struct raid_set *rs = container_of(mddev, struct raid_set, md);
+       uint64_t events_sb;
+       uint64_t failed_devices;
+       struct dm_raid_superblock *sb;
+       uint32_t new_devs = 0;
+       uint32_t rebuilds = 0;
+       mdk_rdev_t *r, *t;
+       struct dm_raid_superblock *sb2;
+
+       sb = page_address(rdev->sb_page);
+       events_sb = le64_to_cpu(sb->events);
+       failed_devices = le64_to_cpu(sb->failed_devices);
+
+       /*
+        * Initialise to 1 if this is a new superblock.
+        */
+       mddev->events = events_sb ? : 1;
+
+       /*
+        * Reshaping is not currently allowed
+        */
+       if ((le32_to_cpu(sb->level) != mddev->level) ||
+           (le32_to_cpu(sb->layout) != mddev->layout) ||
+           (le32_to_cpu(sb->stripe_sectors) != mddev->chunk_sectors)) {
+               DMERR("Reshaping arrays not yet supported.");
+               return -EINVAL;
+       }
+
+       /* We can only change the number of devices in RAID1 right now */
+       if ((rs->raid_type->level != 1) &&
+           (le32_to_cpu(sb->num_devices) != mddev->raid_disks)) {
+               DMERR("Reshaping arrays not yet supported.");
+               return -EINVAL;
+       }
+
+       if (!(rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC)))
+               mddev->recovery_cp = le64_to_cpu(sb->array_resync_offset);
+
+       /*
+        * During load, we set FirstUse if a new superblock was written.
+        * There are two reasons we might not have a superblock:
+        * 1) The array is brand new - in which case, all of the
+        *    devices must have their In_sync bit set.  Also,
+        *    recovery_cp must be 0, unless forced.
+        * 2) This is a new device being added to an old array
+        *    and the new device needs to be rebuilt - in which
+        *    case the In_sync bit will /not/ be set and
+        *    recovery_cp must be MaxSector.
+        */
+       rdev_for_each(r, t, mddev) {
+               if (!test_bit(In_sync, &r->flags)) {
+                       if (!test_bit(FirstUse, &r->flags))
+                               DMERR("Superblock area of "
+                                     "rebuild device %d should have been "
+                                     "cleared.", r->raid_disk);
+                       set_bit(FirstUse, &r->flags);
+                       rebuilds++;
+               } else if (test_bit(FirstUse, &r->flags))
+                       new_devs++;
+       }
+
+       if (!rebuilds) {
+               if (new_devs == mddev->raid_disks) {
+                       DMINFO("Superblocks created for new array");
+                       set_bit(MD_ARRAY_FIRST_USE, &mddev->flags);
+               } else if (new_devs) {
+                       DMERR("New device injected "
+                             "into existing array without 'rebuild' "
+                             "parameter specified");
+                       return -EINVAL;
+               }
+       } else if (new_devs) {
+               DMERR("'rebuild' devices cannot be "
+                     "injected into an array with other first-time devices");
+               return -EINVAL;
+       } else if (mddev->recovery_cp != MaxSector) {
+               DMERR("'rebuild' specified while array is not in-sync");
+               return -EINVAL;
+       }
+
+       /*
+        * Now we set the Faulty bit for those devices that are
+        * recorded in the superblock as failed.
+        */
+       rdev_for_each(r, t, mddev) {
+               if (!r->sb_page)
+                       continue;
+               sb2 = page_address(r->sb_page);
+               sb2->failed_devices = 0;
+
+               /*
+                * Check for any device re-ordering.
+                */
+               if (!test_bit(FirstUse, &r->flags) && (r->raid_disk >= 0)) {
+                       role = le32_to_cpu(sb2->array_position);
+                       if (role != r->raid_disk) {
+                               if (rs->raid_type->level != 1) {
+                                       rs->ti->error = "Cannot change device "
+                                               "positions in RAID array";
+                                       return -EINVAL;
+                               }
+                               DMINFO("RAID1 device #%d now at position #%d",
+                                      role, r->raid_disk);
+                       }
+
+                       /*
+                        * Partial recovery is performed on
+                        * returning failed devices.
+                        */
+                       if (failed_devices & (1 << role))
+                               set_bit(Faulty, &r->flags);
+               }
+       }
+
+       return 0;
+}
+
+static int super_validate(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+       struct dm_raid_superblock *sb = page_address(rdev->sb_page);
+
+       /*
+        * If mddev->events is not set, we know we have not yet initialized
+        * the array.
+        */
+       if (!mddev->events && super_init_validation(mddev, rdev))
+               return -EINVAL;
+
+       mddev->bitmap_info.offset = 4096 >> 9; /* Enable bitmap creation */
+       rdev->mddev->bitmap_info.default_offset = 4096 >> 9;
+       if (!test_bit(FirstUse, &rdev->flags)) {
+               rdev->recovery_offset = le64_to_cpu(sb->disk_recovery_offset);
+               if (rdev->recovery_offset != MaxSector)
+                       clear_bit(In_sync, &rdev->flags);
+       }
+
+       /*
+        * If a device comes back, set it as not In_sync and no longer faulty.
+        */
+       if (test_bit(Faulty, &rdev->flags)) {
+               clear_bit(Faulty, &rdev->flags);
+               clear_bit(In_sync, &rdev->flags);
+               rdev->saved_raid_disk = rdev->raid_disk;
+               rdev->recovery_offset = 0;
+       }
+
+       clear_bit(FirstUse, &rdev->flags);
+
+       return 0;
+}
+
+/*
+ * Analyse superblocks and select the freshest.
+ */
+static int analyse_superblocks(struct dm_target *ti, struct raid_set *rs)
+{
+       int ret;
+       mdk_rdev_t *rdev, *freshest, *tmp;
+       mddev_t *mddev = &rs->md;
+
+       freshest = NULL;
+       rdev_for_each(rdev, tmp, mddev) {
+               if (!rdev->meta_bdev)
+                       continue;
+
+               ret = super_load(rdev, freshest);
+
+               switch (ret) {
+               case 1:
+                       freshest = rdev;
+                       break;
+               case 0:
+                       break;
+               default:
+                       ti->error = "Failed to load superblock";
+                       return ret;
+               }
+       }
+
+       if (!freshest)
+               return 0;
+
+       /*
+        * Validation of the freshest device provides the source of
+        * validation for the remaining devices.
+        */
+       ti->error = "Unable to assemble array: Invalid superblocks";
+       if (super_validate(mddev, freshest))
+               return -EINVAL;
+
+       rdev_for_each(rdev, tmp, mddev)
+               if ((rdev != freshest) && super_validate(mddev, rdev))
+                       return -EINVAL;
+
+       return 0;
+}
+
 /*
  * Construct a RAID4/5/6 mapping:
  * Args:
  *     <raid_type> <#raid_params> <raid_params>                \
  *     <#raid_devs> { <meta_dev1> <dev1> .. <meta_devN> <devN> }
  *
- * ** metadata devices are not supported yet, use '-' instead **
- *
  * <raid_params> varies by <raid_type>.  See 'parse_raid_params' for
  * details on possible <raid_params>.
  */
@@ -465,8 +962,12 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
        if (ret)
                goto bad;
 
+       rs->md.sync_super = super_sync;
+       ret = analyse_superblocks(ti, rs);
+       if (ret)
+               goto bad;
+
        INIT_WORK(&rs->md.event_work, do_table_event);
-       ti->split_io = rs->md.chunk_sectors;
        ti->private = rs;
 
        mutex_lock(&rs->md.reconfig_mutex);
@@ -482,6 +983,7 @@ static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
        rs->callbacks.congested_fn = raid_is_congested;
        dm_table_add_target_callbacks(ti->table, &rs->callbacks);
 
+       mddev_suspend(&rs->md);
        return 0;
 
 bad:
@@ -546,12 +1048,17 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                break;
        case STATUSTYPE_TABLE:
                /* The string you would use to construct this array */
-               for (i = 0; i < rs->md.raid_disks; i++)
-                       if (rs->dev[i].data_dev &&
+               for (i = 0; i < rs->md.raid_disks; i++) {
+                       if ((rs->print_flags & DMPF_REBUILD) &&
+                           rs->dev[i].data_dev &&
                            !test_bit(In_sync, &rs->dev[i].rdev.flags))
-                               raid_param_cnt++; /* for rebuilds */
+                               raid_param_cnt += 2; /* for rebuilds */
+                       if (rs->dev[i].data_dev &&
+                           test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+                               raid_param_cnt += 2;
+               }
 
-               raid_param_cnt += (hweight64(rs->print_flags) * 2);
+               raid_param_cnt += (hweight64(rs->print_flags & ~DMPF_REBUILD) * 2);
                if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
                        raid_param_cnt--;
 
@@ -565,7 +1072,8 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                        DMEMIT(" nosync");
 
                for (i = 0; i < rs->md.raid_disks; i++)
-                       if (rs->dev[i].data_dev &&
+                       if ((rs->print_flags & DMPF_REBUILD) &&
+                           rs->dev[i].data_dev &&
                            !test_bit(In_sync, &rs->dev[i].rdev.flags))
                                DMEMIT(" rebuild %u", i);
 
@@ -579,6 +1087,11 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
                        DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
 
+               for (i = 0; i < rs->md.raid_disks; i++)
+                       if (rs->dev[i].data_dev &&
+                           test_bit(WriteMostly, &rs->dev[i].rdev.flags))
+                               DMEMIT(" write_mostly %u", i);
+
                if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
                        DMEMIT(" max_write_behind %lu",
                               rs->md.bitmap_info.max_write_behind);
@@ -591,9 +1104,16 @@ static int raid_status(struct dm_target *ti, status_type_t type,
                               conf ? conf->max_nr_stripes * 2 : 0);
                }
 
+               if (rs->print_flags & DMPF_REGION_SIZE)
+                       DMEMIT(" region_size %lu",
+                              rs->md.bitmap_info.chunksize >> 9);
+
                DMEMIT(" %d", rs->md.raid_disks);
                for (i = 0; i < rs->md.raid_disks; i++) {
-                       DMEMIT(" -"); /* metadata device */
+                       if (rs->dev[i].meta_dev)
+                               DMEMIT(" %s", rs->dev[i].meta_dev->name);
+                       else
+                               DMEMIT(" -");
 
                        if (rs->dev[i].data_dev)
                                DMEMIT(" %s", rs->dev[i].data_dev->name);
@@ -650,12 +1170,13 @@ static void raid_resume(struct dm_target *ti)
 {
        struct raid_set *rs = ti->private;
 
+       bitmap_load(&rs->md);
        mddev_resume(&rs->md);
 }
 
 static struct target_type raid_target = {
        .name = "raid",
-       .version = {1, 0, 0},
+       .version = {1, 1, 0},
        .module = THIS_MODULE,
        .ctr = raid_ctr,
        .dtr = raid_dtr,
index 135c2f1fdbfcc95aefb0d842030e71a089e87d30..d1f1d70171038cf9836832b68c63ad4b1d15297a 100644 (file)
 #define NUM_SNAPSHOT_HDR_CHUNKS 1
 
 struct disk_header {
-       uint32_t magic;
+       __le32 magic;
 
        /*
         * Is this snapshot valid.  There is no way of recovering
         * an invalid snapshot.
         */
-       uint32_t valid;
+       __le32 valid;
 
        /*
         * Simple, incrementing version. no backward
         * compatibility.
         */
-       uint32_t version;
+       __le32 version;
 
        /* In sectors */
-       uint32_t chunk_size;
-};
+       __le32 chunk_size;
+} __packed;
 
 struct disk_exception {
+       __le64 old_chunk;
+       __le64 new_chunk;
+} __packed;
+
+struct core_exception {
        uint64_t old_chunk;
        uint64_t new_chunk;
 };
@@ -169,10 +174,9 @@ static int alloc_area(struct pstore *ps)
        if (!ps->area)
                goto err_area;
 
-       ps->zero_area = vmalloc(len);
+       ps->zero_area = vzalloc(len);
        if (!ps->zero_area)
                goto err_zero_area;
-       memset(ps->zero_area, 0, len);
 
        ps->header_area = vmalloc(len);
        if (!ps->header_area)
@@ -396,32 +400,32 @@ static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
 }
 
 static void read_exception(struct pstore *ps,
-                          uint32_t index, struct disk_exception *result)
+                          uint32_t index, struct core_exception *result)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* copy it */
-       result->old_chunk = le64_to_cpu(e->old_chunk);
-       result->new_chunk = le64_to_cpu(e->new_chunk);
+       result->old_chunk = le64_to_cpu(de->old_chunk);
+       result->new_chunk = le64_to_cpu(de->new_chunk);
 }
 
 static void write_exception(struct pstore *ps,
-                           uint32_t index, struct disk_exception *de)
+                           uint32_t index, struct core_exception *e)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* copy it */
-       e->old_chunk = cpu_to_le64(de->old_chunk);
-       e->new_chunk = cpu_to_le64(de->new_chunk);
+       de->old_chunk = cpu_to_le64(e->old_chunk);
+       de->new_chunk = cpu_to_le64(e->new_chunk);
 }
 
 static void clear_exception(struct pstore *ps, uint32_t index)
 {
-       struct disk_exception *e = get_exception(ps, index);
+       struct disk_exception *de = get_exception(ps, index);
 
        /* clear it */
-       e->old_chunk = 0;
-       e->new_chunk = 0;
+       de->old_chunk = 0;
+       de->new_chunk = 0;
 }
 
 /*
@@ -437,13 +441,13 @@ static int insert_exceptions(struct pstore *ps,
 {
        int r;
        unsigned int i;
-       struct disk_exception de;
+       struct core_exception e;
 
        /* presume the area is full */
        *full = 1;
 
        for (i = 0; i < ps->exceptions_per_area; i++) {
-               read_exception(ps, i, &de);
+               read_exception(ps, i, &e);
 
                /*
                 * If the new_chunk is pointing at the start of
@@ -451,7 +455,7 @@ static int insert_exceptions(struct pstore *ps,
                 * is we know that we've hit the end of the
                 * exceptions.  Therefore the area is not full.
                 */
-               if (de.new_chunk == 0LL) {
+               if (e.new_chunk == 0LL) {
                        ps->current_committed = i;
                        *full = 0;
                        break;
@@ -460,13 +464,13 @@ static int insert_exceptions(struct pstore *ps,
                /*
                 * Keep track of the start of the free chunks.
                 */
-               if (ps->next_free <= de.new_chunk)
-                       ps->next_free = de.new_chunk + 1;
+               if (ps->next_free <= e.new_chunk)
+                       ps->next_free = e.new_chunk + 1;
 
                /*
                 * Otherwise we add the exception to the snapshot.
                 */
-               r = callback(callback_context, de.old_chunk, de.new_chunk);
+               r = callback(callback_context, e.old_chunk, e.new_chunk);
                if (r)
                        return r;
        }
@@ -563,7 +567,7 @@ static int persistent_read_metadata(struct dm_exception_store *store,
        ps->exceptions_per_area = (ps->store->chunk_size << SECTOR_SHIFT) /
                                  sizeof(struct disk_exception);
        ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
-                       sizeof(*ps->callbacks));
+                                  sizeof(*ps->callbacks));
        if (!ps->callbacks)
                return -ENOMEM;
 
@@ -641,12 +645,12 @@ static void persistent_commit_exception(struct dm_exception_store *store,
 {
        unsigned int i;
        struct pstore *ps = get_info(store);
-       struct disk_exception de;
+       struct core_exception ce;
        struct commit_callback *cb;
 
-       de.old_chunk = e->old_chunk;
-       de.new_chunk = e->new_chunk;
-       write_exception(ps, ps->current_committed++, &de);
+       ce.old_chunk = e->old_chunk;
+       ce.new_chunk = e->new_chunk;
+       write_exception(ps, ps->current_committed++, &ce);
 
        /*
         * Add the callback to the back of the array.  This code
@@ -670,7 +674,7 @@ static void persistent_commit_exception(struct dm_exception_store *store,
         * If we completely filled the current area, then wipe the next one.
         */
        if ((ps->current_committed == ps->exceptions_per_area) &&
-            zero_disk_area(ps, ps->current_area + 1))
+           zero_disk_area(ps, ps->current_area + 1))
                ps->valid = 0;
 
        /*
@@ -701,7 +705,7 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
                                    chunk_t *last_new_chunk)
 {
        struct pstore *ps = get_info(store);
-       struct disk_exception de;
+       struct core_exception ce;
        int nr_consecutive;
        int r;
 
@@ -722,9 +726,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
                ps->current_committed = ps->exceptions_per_area;
        }
 
-       read_exception(ps, ps->current_committed - 1, &de);
-       *last_old_chunk = de.old_chunk;
-       *last_new_chunk = de.new_chunk;
+       read_exception(ps, ps->current_committed - 1, &ce);
+       *last_old_chunk = ce.old_chunk;
+       *last_new_chunk = ce.new_chunk;
 
        /*
         * Find number of consecutive chunks within the current area,
@@ -733,9 +737,9 @@ static int persistent_prepare_merge(struct dm_exception_store *store,
        for (nr_consecutive = 1; nr_consecutive < ps->current_committed;
             nr_consecutive++) {
                read_exception(ps, ps->current_committed - 1 - nr_consecutive,
-                              &de);
-               if (de.old_chunk != *last_old_chunk - nr_consecutive ||
-                   de.new_chunk != *last_new_chunk - nr_consecutive)
+                              &ce);
+               if (ce.old_chunk != *last_old_chunk - nr_consecutive ||
+                   ce.new_chunk != *last_new_chunk - nr_consecutive)
                        break;
        }
 
@@ -753,7 +757,7 @@ static int persistent_commit_merge(struct dm_exception_store *store,
        for (i = 0; i < nr_merged; i++)
                clear_exception(ps, ps->current_committed - 1 - i);
 
-       r = area_io(ps, WRITE);
+       r = area_io(ps, WRITE_FLUSH_FUA);
        if (r < 0)
                return r;
 
index 9ecff5f3023a4c4f0721b958ef1541e762e910ff..6f758870fc19cf0e66db3055dab36ab96e038fee 100644 (file)
@@ -29,16 +29,6 @@ static const char dm_snapshot_merge_target_name[] = "snapshot-merge";
 #define dm_target_is_snapshot_merge(ti) \
        ((ti)->type->name == dm_snapshot_merge_target_name)
 
-/*
- * The percentage increment we will wake up users at
- */
-#define WAKE_UP_PERCENT 5
-
-/*
- * kcopyd priority of snapshot operations
- */
-#define SNAPSHOT_COPY_PRIORITY 2
-
 /*
  * The size of the mempool used to track chunks in use.
  */
@@ -180,6 +170,13 @@ struct dm_snap_pending_exception {
         * kcopyd.
         */
        int started;
+
+       /*
+        * For writing a complete chunk, bypassing the copy.
+        */
+       struct bio *full_bio;
+       bio_end_io_t *full_bio_end_io;
+       void *full_bio_private;
 };
 
 /*
@@ -1055,8 +1052,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
        s = kmalloc(sizeof(*s), GFP_KERNEL);
        if (!s) {
-               ti->error = "Cannot allocate snapshot context private "
-                   "structure";
+               ti->error = "Cannot allocate private snapshot structure";
                r = -ENOMEM;
                goto bad;
        }
@@ -1380,6 +1376,7 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
        struct dm_snapshot *s = pe->snap;
        struct bio *origin_bios = NULL;
        struct bio *snapshot_bios = NULL;
+       struct bio *full_bio = NULL;
        int error = 0;
 
        if (!success) {
@@ -1415,10 +1412,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
         */
        dm_insert_exception(&s->complete, e);
 
- out:
+out:
        dm_remove_exception(&pe->e);
        snapshot_bios = bio_list_get(&pe->snapshot_bios);
        origin_bios = bio_list_get(&pe->origin_bios);
+       full_bio = pe->full_bio;
+       if (full_bio) {
+               full_bio->bi_end_io = pe->full_bio_end_io;
+               full_bio->bi_private = pe->full_bio_private;
+       }
        free_pending_exception(pe);
 
        increment_pending_exceptions_done_count();
@@ -1426,10 +1428,15 @@ static void pending_complete(struct dm_snap_pending_exception *pe, int success)
        up_write(&s->lock);
 
        /* Submit any pending write bios */
-       if (error)
+       if (error) {
+               if (full_bio)
+                       bio_io_error(full_bio);
                error_bios(snapshot_bios);
-       else
+       } else {
+               if (full_bio)
+                       bio_endio(full_bio, 0);
                flush_bios(snapshot_bios);
+       }
 
        retry_origin_bios(s, origin_bios);
 }
@@ -1480,8 +1487,33 @@ static void start_copy(struct dm_snap_pending_exception *pe)
        dest.count = src.count;
 
        /* Hand over to kcopyd */
-       dm_kcopyd_copy(s->kcopyd_client,
-                   &src, 1, &dest, 0, copy_callback, pe);
+       dm_kcopyd_copy(s->kcopyd_client, &src, 1, &dest, 0, copy_callback, pe);
+}
+
+static void full_bio_end_io(struct bio *bio, int error)
+{
+       void *callback_data = bio->bi_private;
+
+       dm_kcopyd_do_callback(callback_data, 0, error ? 1 : 0);
+}
+
+static void start_full_bio(struct dm_snap_pending_exception *pe,
+                          struct bio *bio)
+{
+       struct dm_snapshot *s = pe->snap;
+       void *callback_data;
+
+       pe->full_bio = bio;
+       pe->full_bio_end_io = bio->bi_end_io;
+       pe->full_bio_private = bio->bi_private;
+
+       callback_data = dm_kcopyd_prepare_callback(s->kcopyd_client,
+                                                  copy_callback, pe);
+
+       bio->bi_end_io = full_bio_end_io;
+       bio->bi_private = callback_data;
+
+       generic_make_request(bio);
 }
 
 static struct dm_snap_pending_exception *
@@ -1519,6 +1551,7 @@ __find_pending_exception(struct dm_snapshot *s,
        bio_list_init(&pe->origin_bios);
        bio_list_init(&pe->snapshot_bios);
        pe->started = 0;
+       pe->full_bio = NULL;
 
        if (s->store->type->prepare_exception(s->store, &pe->e)) {
                free_pending_exception(pe);
@@ -1612,10 +1645,19 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
                }
 
                remap_exception(s, &pe->e, bio, chunk);
-               bio_list_add(&pe->snapshot_bios, bio);
 
                r = DM_MAPIO_SUBMITTED;
 
+               if (!pe->started &&
+                   bio->bi_size == (s->store->chunk_size << SECTOR_SHIFT)) {
+                       pe->started = 1;
+                       up_write(&s->lock);
+                       start_full_bio(pe, bio);
+                       goto out;
+               }
+
+               bio_list_add(&pe->snapshot_bios, bio);
+
                if (!pe->started) {
                        /* this is protected by snap->lock */
                        pe->started = 1;
@@ -1628,9 +1670,9 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
                map_context->ptr = track_chunk(s, chunk);
        }
 
- out_unlock:
+out_unlock:
        up_write(&s->lock);
- out:
+out:
        return r;
 }
 
@@ -1974,7 +2016,7 @@ static int __origin_write(struct list_head *snapshots, sector_t sector,
                        pe_to_start_now = pe;
                }
 
- next_snapshot:
+next_snapshot:
                up_write(&snap->lock);
 
                if (pe_to_start_now) {
index bfe9c2333ceacecddd00d058615fc8a9f2867b1b..986b8754bb0813c59fb22a0feae7083740378f72 100644 (file)
@@ -54,7 +54,6 @@ struct dm_table {
        sector_t *highs;
        struct dm_target *targets;
 
-       unsigned discards_supported:1;
        unsigned integrity_supported:1;
 
        /*
@@ -154,12 +153,11 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size)
                return NULL;
 
        size = nmemb * elem_size;
-       addr = vmalloc(size);
-       if (addr)
-               memset(addr, 0, size);
+       addr = vzalloc(size);
 
        return addr;
 }
+EXPORT_SYMBOL(dm_vcalloc);
 
 /*
  * highs, and targets are managed as dynamic arrays during a
@@ -209,7 +207,6 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
        INIT_LIST_HEAD(&t->devices);
        INIT_LIST_HEAD(&t->target_callbacks);
        atomic_set(&t->holders, 0);
-       t->discards_supported = 1;
 
        if (!num_targets)
                num_targets = KEYS_PER_NODE;
@@ -281,6 +278,7 @@ void dm_table_get(struct dm_table *t)
 {
        atomic_inc(&t->holders);
 }
+EXPORT_SYMBOL(dm_table_get);
 
 void dm_table_put(struct dm_table *t)
 {
@@ -290,6 +288,7 @@ void dm_table_put(struct dm_table *t)
        smp_mb__before_atomic_dec();
        atomic_dec(&t->holders);
 }
+EXPORT_SYMBOL(dm_table_put);
 
 /*
  * Checks to see if we need to extend highs or targets.
@@ -455,13 +454,14 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
  * Add a device to the list, or just increment the usage count if
  * it's already present.
  */
-static int __table_get_device(struct dm_table *t, struct dm_target *ti,
-                     const char *path, fmode_t mode, struct dm_dev **result)
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+                 struct dm_dev **result)
 {
        int r;
        dev_t uninitialized_var(dev);
        struct dm_dev_internal *dd;
        unsigned int major, minor;
+       struct dm_table *t = ti->table;
 
        BUG_ON(!t);
 
@@ -509,6 +509,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
        *result = &dd->dm_dev;
        return 0;
 }
+EXPORT_SYMBOL(dm_get_device);
 
 int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
                         sector_t start, sector_t len, void *data)
@@ -539,23 +540,15 @@ int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
         * If not we'll force DM to use PAGE_SIZE or
         * smaller I/O, just to be safe.
         */
-
-       if (q->merge_bvec_fn && !ti->type->merge)
+       if (dm_queue_merge_is_compulsory(q) && !ti->type->merge)
                blk_limits_max_hw_sectors(limits,
                                          (unsigned int) (PAGE_SIZE >> 9));
        return 0;
 }
 EXPORT_SYMBOL_GPL(dm_set_device_limits);
 
-int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
-                 struct dm_dev **result)
-{
-       return __table_get_device(ti->table, ti, path, mode, result);
-}
-
-
 /*
- * Decrement a devices use count and remove it if necessary.
+ * Decrement a device's use count and remove it if necessary.
  */
 void dm_put_device(struct dm_target *ti, struct dm_dev *d)
 {
@@ -568,6 +561,7 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d)
                kfree(dd);
        }
 }
+EXPORT_SYMBOL(dm_put_device);
 
 /*
  * Checks to see if the target joins onto the end of the table.
@@ -791,8 +785,9 @@ int dm_table_add_target(struct dm_table *t, const char *type,
 
        t->highs[t->num_targets++] = tgt->begin + tgt->len - 1;
 
-       if (!tgt->num_discard_requests)
-               t->discards_supported = 0;
+       if (!tgt->num_discard_requests && tgt->discards_supported)
+               DMWARN("%s: %s: ignoring discards_supported because num_discard_requests is zero.",
+                      dm_device_name(t->md), type);
 
        return 0;
 
@@ -802,6 +797,63 @@ int dm_table_add_target(struct dm_table *t, const char *type,
        return r;
 }
 
+/*
+ * Target argument parsing helpers.
+ */
+static int validate_next_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+                            unsigned *value, char **error, unsigned grouped)
+{
+       const char *arg_str = dm_shift_arg(arg_set);
+
+       if (!arg_str ||
+           (sscanf(arg_str, "%u", value) != 1) ||
+           (*value < arg->min) ||
+           (*value > arg->max) ||
+           (grouped && arg_set->argc < *value)) {
+               *error = arg->error;
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+int dm_read_arg(struct dm_arg *arg, struct dm_arg_set *arg_set,
+               unsigned *value, char **error)
+{
+       return validate_next_arg(arg, arg_set, value, error, 0);
+}
+EXPORT_SYMBOL(dm_read_arg);
+
+int dm_read_arg_group(struct dm_arg *arg, struct dm_arg_set *arg_set,
+                     unsigned *value, char **error)
+{
+       return validate_next_arg(arg, arg_set, value, error, 1);
+}
+EXPORT_SYMBOL(dm_read_arg_group);
+
+const char *dm_shift_arg(struct dm_arg_set *as)
+{
+       char *r;
+
+       if (as->argc) {
+               as->argc--;
+               r = *as->argv;
+               as->argv++;
+               return r;
+       }
+
+       return NULL;
+}
+EXPORT_SYMBOL(dm_shift_arg);
+
+void dm_consume_args(struct dm_arg_set *as, unsigned num_args)
+{
+       BUG_ON(as->argc < num_args);
+       as->argc -= num_args;
+       as->argv += num_args;
+}
+EXPORT_SYMBOL(dm_consume_args);
+
 static int dm_table_set_type(struct dm_table *t)
 {
        unsigned i;
@@ -1077,11 +1129,13 @@ void dm_table_event(struct dm_table *t)
                t->event_fn(t->event_context);
        mutex_unlock(&_event_lock);
 }
+EXPORT_SYMBOL(dm_table_event);
 
 sector_t dm_table_get_size(struct dm_table *t)
 {
        return t->num_targets ? (t->highs[t->num_targets - 1] + 1) : 0;
 }
+EXPORT_SYMBOL(dm_table_get_size);
 
 struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
 {
@@ -1194,9 +1248,45 @@ static void dm_table_set_integrity(struct dm_table *t)
                               blk_get_integrity(template_disk));
 }
 
+static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
+                               sector_t start, sector_t len, void *data)
+{
+       unsigned flush = (*(unsigned *)data);
+       struct request_queue *q = bdev_get_queue(dev->bdev);
+
+       return q && (q->flush_flags & flush);
+}
+
+static bool dm_table_supports_flush(struct dm_table *t, unsigned flush)
+{
+       struct dm_target *ti;
+       unsigned i = 0;
+
+       /*
+        * Require at least one underlying device to support flushes.
+        * t->devices includes internal dm devices such as mirror logs
+        * so we need to use iterate_devices here, which targets
+        * supporting flushes must provide.
+        */
+       while (i < dm_table_get_num_targets(t)) {
+               ti = dm_table_get_target(t, i++);
+
+               if (!ti->num_flush_requests)
+                       continue;
+
+               if (ti->type->iterate_devices &&
+                   ti->type->iterate_devices(ti, device_flush_capable, &flush))
+                       return 1;
+       }
+
+       return 0;
+}
+
 void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
                               struct queue_limits *limits)
 {
+       unsigned flush = 0;
+
        /*
         * Copy table's limits to the DM device's request_queue
         */
@@ -1207,6 +1297,13 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q,
        else
                queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, q);
 
+       if (dm_table_supports_flush(t, REQ_FLUSH)) {
+               flush |= REQ_FLUSH;
+               if (dm_table_supports_flush(t, REQ_FUA))
+                       flush |= REQ_FUA;
+       }
+       blk_queue_flush(q, flush);
+
        dm_table_set_integrity(t);
 
        /*
@@ -1237,6 +1334,7 @@ fmode_t dm_table_get_mode(struct dm_table *t)
 {
        return t->mode;
 }
+EXPORT_SYMBOL(dm_table_get_mode);
 
 static void suspend_targets(struct dm_table *t, unsigned postsuspend)
 {
@@ -1345,6 +1443,7 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
 {
        return t->md;
 }
+EXPORT_SYMBOL(dm_table_get_md);
 
 static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
                                  sector_t start, sector_t len, void *data)
@@ -1359,19 +1458,19 @@ bool dm_table_supports_discards(struct dm_table *t)
        struct dm_target *ti;
        unsigned i = 0;
 
-       if (!t->discards_supported)
-               return 0;
-
        /*
         * Unless any target used by the table set discards_supported,
         * require at least one underlying device to support discards.
         * t->devices includes internal dm devices such as mirror logs
         * so we need to use iterate_devices here, which targets
-        * supporting discard must provide.
+        * supporting discard selectively must provide.
         */
        while (i < dm_table_get_num_targets(t)) {
                ti = dm_table_get_target(t, i++);
 
+               if (!ti->num_discard_requests)
+                       continue;
+
                if (ti->discards_supported)
                        return 1;
 
@@ -1382,13 +1481,3 @@ bool dm_table_supports_discards(struct dm_table *t)
 
        return 0;
 }
-
-EXPORT_SYMBOL(dm_vcalloc);
-EXPORT_SYMBOL(dm_get_device);
-EXPORT_SYMBOL(dm_put_device);
-EXPORT_SYMBOL(dm_table_event);
-EXPORT_SYMBOL(dm_table_get_size);
-EXPORT_SYMBOL(dm_table_get_mode);
-EXPORT_SYMBOL(dm_table_get_md);
-EXPORT_SYMBOL(dm_table_put);
-EXPORT_SYMBOL(dm_table_get);
index 0cf68b478878f327598fb756fe2da173d5443468..52b39f335bb38549045f46eae8cad3714c867c5d 100644 (file)
@@ -37,6 +37,8 @@ static const char *_name = DM_NAME;
 static unsigned int major = 0;
 static unsigned int _major = 0;
 
+static DEFINE_IDR(_minor_idr);
+
 static DEFINE_SPINLOCK(_minor_lock);
 /*
  * For bio-based dm.
@@ -109,6 +111,7 @@ EXPORT_SYMBOL_GPL(dm_get_rq_mapinfo);
 #define DMF_FREEING 3
 #define DMF_DELETING 4
 #define DMF_NOFLUSH_SUSPENDING 5
+#define DMF_MERGE_IS_OPTIONAL 6
 
 /*
  * Work processed by per-device workqueue.
@@ -313,6 +316,12 @@ static void __exit dm_exit(void)
 
        while (i--)
                _exits[i]();
+
+       /*
+        * Should be empty by this point.
+        */
+       idr_remove_all(&_minor_idr);
+       idr_destroy(&_minor_idr);
 }
 
 /*
@@ -1171,7 +1180,8 @@ static int __clone_and_map_discard(struct clone_info *ci)
 
                /*
                 * Even though the device advertised discard support,
-                * reconfiguration might have changed that since the
+                * that does not mean every target supports it, and
+                * reconfiguration might also have changed that since the
                 * check was performed.
                 */
                if (!ti->num_discard_requests)
@@ -1705,8 +1715,6 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
 /*-----------------------------------------------------------------
  * An IDR is used to keep track of allocated minor numbers.
  *---------------------------------------------------------------*/
-static DEFINE_IDR(_minor_idr);
-
 static void free_minor(int minor)
 {
        spin_lock(&_minor_lock);
@@ -1800,7 +1808,6 @@ static void dm_init_md_queue(struct mapped_device *md)
        blk_queue_make_request(md->queue, dm_request);
        blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
        blk_queue_merge_bvec(md->queue, dm_merge_bvec);
-       blk_queue_flush(md->queue, REQ_FLUSH | REQ_FUA);
 }
 
 /*
@@ -1985,6 +1992,59 @@ static void __set_size(struct mapped_device *md, sector_t size)
        i_size_write(md->bdev->bd_inode, (loff_t)size << SECTOR_SHIFT);
 }
 
+/*
+ * Return 1 if the queue has a compulsory merge_bvec_fn function.
+ *
+ * If this function returns 0, then the device is either a non-dm
+ * device without a merge_bvec_fn, or it is a dm device that is
+ * able to split any bios it receives that are too big.
+ */
+int dm_queue_merge_is_compulsory(struct request_queue *q)
+{
+       struct mapped_device *dev_md;
+
+       if (!q->merge_bvec_fn)
+               return 0;
+
+       if (q->make_request_fn == dm_request) {
+               dev_md = q->queuedata;
+               if (test_bit(DMF_MERGE_IS_OPTIONAL, &dev_md->flags))
+                       return 0;
+       }
+
+       return 1;
+}
+
+static int dm_device_merge_is_compulsory(struct dm_target *ti,
+                                        struct dm_dev *dev, sector_t start,
+                                        sector_t len, void *data)
+{
+       struct block_device *bdev = dev->bdev;
+       struct request_queue *q = bdev_get_queue(bdev);
+
+       return dm_queue_merge_is_compulsory(q);
+}
+
+/*
+ * Return 1 if it is acceptable to ignore merge_bvec_fn based
+ * on the properties of the underlying devices.
+ */
+static int dm_table_merge_is_optional(struct dm_table *table)
+{
+       unsigned i = 0;
+       struct dm_target *ti;
+
+       while (i < dm_table_get_num_targets(table)) {
+               ti = dm_table_get_target(table, i++);
+
+               if (ti->type->iterate_devices &&
+                   ti->type->iterate_devices(ti, dm_device_merge_is_compulsory, NULL))
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Returns old map, which caller must destroy.
  */
@@ -1995,6 +2055,7 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
        struct request_queue *q = md->queue;
        sector_t size;
        unsigned long flags;
+       int merge_is_optional;
 
        size = dm_table_get_size(t);
 
@@ -2020,10 +2081,16 @@ static struct dm_table *__bind(struct mapped_device *md, struct dm_table *t,
 
        __bind_mempools(md, t);
 
+       merge_is_optional = dm_table_merge_is_optional(t);
+
        write_lock_irqsave(&md->map_lock, flags);
        old_map = md->map;
        md->map = t;
        dm_table_set_restrictions(t, q, limits);
+       if (merge_is_optional)
+               set_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
+       else
+               clear_bit(DMF_MERGE_IS_OPTIONAL, &md->flags);
        write_unlock_irqrestore(&md->map_lock, flags);
 
        return old_map;
index 1aaf16746da86f6bfb9c9ffaab334841c044d92a..6745dbd278a4ffc463188c72cab4f301a74adc74 100644 (file)
@@ -66,6 +66,8 @@ int dm_table_alloc_md_mempools(struct dm_table *t);
 void dm_table_free_md_mempools(struct dm_table *t);
 struct dm_md_mempools *dm_table_get_md_mempools(struct dm_table *t);
 
+int dm_queue_merge_is_compulsory(struct request_queue *q);
+
 void dm_lock_md_type(struct mapped_device *md);
 void dm_unlock_md_type(struct mapped_device *md);
 void dm_set_md_type(struct mapped_device *md, unsigned type);
index d3e38790906ed889de2b78454516ec1410eb6466..d8e6a429e8ba046027e9fa8550be6444f47ae971 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/debugfs.h>
 #include <linux/device.h>
 #include <linux/slab.h>
+#include <linux/async.h>
 #include <linux/err.h>
 #include <linux/mutex.h>
 #include <linux/suspend.h>
@@ -33,6 +34,8 @@
 
 #include "dummy.h"
 
+#define rdev_crit(rdev, fmt, ...)                                      \
+       pr_crit("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
 #define rdev_err(rdev, fmt, ...)                                       \
        pr_err("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
 #define rdev_warn(rdev, fmt, ...)                                      \
@@ -78,11 +81,13 @@ struct regulator {
        char *supply_name;
        struct device_attribute dev_attr;
        struct regulator_dev *rdev;
+#ifdef CONFIG_DEBUG_FS
+       struct dentry *debugfs;
+#endif
 };
 
 static int _regulator_is_enabled(struct regulator_dev *rdev);
-static int _regulator_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr);
+static int _regulator_disable(struct regulator_dev *rdev);
 static int _regulator_get_voltage(struct regulator_dev *rdev);
 static int _regulator_get_current_limit(struct regulator_dev *rdev);
 static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
@@ -90,6 +95,9 @@ static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data);
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                                     int min_uV, int max_uV);
+static struct regulator *create_regulator(struct regulator_dev *rdev,
+                                         struct device *dev,
+                                         const char *supply_name);
 
 static const char *rdev_get_name(struct regulator_dev *rdev)
 {
@@ -143,8 +151,11 @@ static int regulator_check_voltage(struct regulator_dev *rdev,
        if (*min_uV < rdev->constraints->min_uV)
                *min_uV = rdev->constraints->min_uV;
 
-       if (*min_uV > *max_uV)
+       if (*min_uV > *max_uV) {
+               rdev_err(rdev, "unsupportable voltage range: %d-%duV\n",
+                        *min_uV, *max_uV);
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -197,8 +208,11 @@ static int regulator_check_current_limit(struct regulator_dev *rdev,
        if (*min_uA < rdev->constraints->min_uA)
                *min_uA = rdev->constraints->min_uA;
 
-       if (*min_uA > *max_uA)
+       if (*min_uA > *max_uA) {
+               rdev_err(rdev, "unsupportable current range: %d-%duA\n",
+                        *min_uA, *max_uA);
                return -EINVAL;
+       }
 
        return 0;
 }
@@ -213,6 +227,7 @@ static int regulator_mode_constrain(struct regulator_dev *rdev, int *mode)
        case REGULATOR_MODE_STANDBY:
                break;
        default:
+               rdev_err(rdev, "invalid mode %x specified\n", *mode);
                return -EINVAL;
        }
 
@@ -779,7 +794,6 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
                if (ret < 0) {
                        rdev_err(rdev, "failed to apply %duV constraint\n",
                                 rdev->constraints->min_uV);
-                       rdev->constraints = NULL;
                        return ret;
                }
        }
@@ -882,7 +896,6 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                ret = suspend_prepare(rdev, rdev->constraints->initial_state);
                if (ret < 0) {
                        rdev_err(rdev, "failed to set suspend state\n");
-                       rdev->constraints = NULL;
                        goto out;
                }
        }
@@ -909,13 +922,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                ret = ops->enable(rdev);
                if (ret < 0) {
                        rdev_err(rdev, "failed to enable\n");
-                       rdev->constraints = NULL;
                        goto out;
                }
        }
 
        print_constraints(rdev);
+       return 0;
 out:
+       kfree(rdev->constraints);
+       rdev->constraints = NULL;
        return ret;
 }
 
@@ -929,21 +944,20 @@ out:
  * core if it's child is enabled.
  */
 static int set_supply(struct regulator_dev *rdev,
-       struct regulator_dev *supply_rdev)
+                     struct regulator_dev *supply_rdev)
 {
        int err;
 
-       err = sysfs_create_link(&rdev->dev.kobj, &supply_rdev->dev.kobj,
-                               "supply");
-       if (err) {
-               rdev_err(rdev, "could not add device link %s err %d\n",
-                        supply_rdev->dev.kobj.name, err);
-                      goto out;
+       rdev_info(rdev, "supplied by %s\n", rdev_get_name(supply_rdev));
+
+       rdev->supply = create_regulator(supply_rdev, &rdev->dev, "SUPPLY");
+       if (IS_ERR(rdev->supply)) {
+               err = PTR_ERR(rdev->supply);
+               rdev->supply = NULL;
+               return err;
        }
-       rdev->supply = supply_rdev;
-       list_add(&rdev->slist, &supply_rdev->supply_list);
-out:
-       return err;
+
+       return 0;
 }
 
 /**
@@ -1032,7 +1046,7 @@ static void unset_regulator_supplies(struct regulator_dev *rdev)
        }
 }
 
-#define REG_STR_SIZE   32
+#define REG_STR_SIZE   64
 
 static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          struct device *dev,
@@ -1052,8 +1066,9 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
 
        if (dev) {
                /* create a 'requested_microamps_name' sysfs entry */
-               size = scnprintf(buf, REG_STR_SIZE, "microamps_requested_%s",
-                       supply_name);
+               size = scnprintf(buf, REG_STR_SIZE,
+                                "microamps_requested_%s-%s",
+                                dev_name(dev), supply_name);
                if (size >= REG_STR_SIZE)
                        goto overflow_err;
 
@@ -1088,7 +1103,28 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                                  dev->kobj.name, err);
                        goto link_name_err;
                }
+       } else {
+               regulator->supply_name = kstrdup(supply_name, GFP_KERNEL);
+               if (regulator->supply_name == NULL)
+                       goto attr_err;
+       }
+
+#ifdef CONFIG_DEBUG_FS
+       regulator->debugfs = debugfs_create_dir(regulator->supply_name,
+                                               rdev->debugfs);
+       if (IS_ERR_OR_NULL(regulator->debugfs)) {
+               rdev_warn(rdev, "Failed to create debugfs directory\n");
+               regulator->debugfs = NULL;
+       } else {
+               debugfs_create_u32("uA_load", 0444, regulator->debugfs,
+                                  &regulator->uA_load);
+               debugfs_create_u32("min_uV", 0444, regulator->debugfs,
+                                  &regulator->min_uV);
+               debugfs_create_u32("max_uV", 0444, regulator->debugfs,
+                                  &regulator->max_uV);
        }
+#endif
+
        mutex_unlock(&rdev->mutex);
        return regulator;
 link_name_err:
@@ -1267,13 +1303,17 @@ void regulator_put(struct regulator *regulator)
        mutex_lock(&regulator_list_mutex);
        rdev = regulator->rdev;
 
+#ifdef CONFIG_DEBUG_FS
+       debugfs_remove_recursive(regulator->debugfs);
+#endif
+
        /* remove any sysfs entries */
        if (regulator->dev) {
                sysfs_remove_link(&rdev->dev.kobj, regulator->supply_name);
-               kfree(regulator->supply_name);
                device_remove_file(regulator->dev, &regulator->dev_attr);
                kfree(regulator->dev_attr.attr.name);
        }
+       kfree(regulator->supply_name);
        list_del(&regulator->list);
        kfree(regulator);
 
@@ -1301,19 +1341,6 @@ static int _regulator_enable(struct regulator_dev *rdev)
 {
        int ret, delay;
 
-       if (rdev->use_count == 0) {
-               /* do we need to enable the supply regulator first */
-               if (rdev->supply) {
-                       mutex_lock(&rdev->supply->mutex);
-                       ret = _regulator_enable(rdev->supply);
-                       mutex_unlock(&rdev->supply->mutex);
-                       if (ret < 0) {
-                               rdev_err(rdev, "failed to enable: %d\n", ret);
-                               return ret;
-                       }
-               }
-       }
-
        /* check voltage and requested load before enabling */
        if (rdev->constraints &&
            (rdev->constraints->valid_ops_mask & REGULATOR_CHANGE_DRMS))
@@ -1388,19 +1415,27 @@ int regulator_enable(struct regulator *regulator)
        struct regulator_dev *rdev = regulator->rdev;
        int ret = 0;
 
+       if (rdev->supply) {
+               ret = regulator_enable(rdev->supply);
+               if (ret != 0)
+                       return ret;
+       }
+
        mutex_lock(&rdev->mutex);
        ret = _regulator_enable(rdev);
        mutex_unlock(&rdev->mutex);
+
+       if (ret != 0)
+               regulator_disable(rdev->supply);
+
        return ret;
 }
 EXPORT_SYMBOL_GPL(regulator_enable);
 
 /* locks held by regulator_disable() */
-static int _regulator_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr)
+static int _regulator_disable(struct regulator_dev *rdev)
 {
        int ret = 0;
-       *supply_rdev_ptr = NULL;
 
        if (WARN(rdev->use_count <= 0,
                 "unbalanced disables for %s\n", rdev_get_name(rdev)))
@@ -1427,9 +1462,6 @@ static int _regulator_disable(struct regulator_dev *rdev,
                                             NULL);
                }
 
-               /* decrease our supplies ref count and disable if required */
-               *supply_rdev_ptr = rdev->supply;
-
                rdev->use_count = 0;
        } else if (rdev->use_count > 1) {
 
@@ -1440,6 +1472,7 @@ static int _regulator_disable(struct regulator_dev *rdev,
 
                rdev->use_count--;
        }
+
        return ret;
 }
 
@@ -1458,29 +1491,21 @@ static int _regulator_disable(struct regulator_dev *rdev,
 int regulator_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       struct regulator_dev *supply_rdev = NULL;
        int ret = 0;
 
        mutex_lock(&rdev->mutex);
-       ret = _regulator_disable(rdev, &supply_rdev);
+       ret = _regulator_disable(rdev);
        mutex_unlock(&rdev->mutex);
 
-       /* decrease our supplies ref count and disable if required */
-       while (supply_rdev != NULL) {
-               rdev = supply_rdev;
-
-               mutex_lock(&rdev->mutex);
-               _regulator_disable(rdev, &supply_rdev);
-               mutex_unlock(&rdev->mutex);
-       }
+       if (ret == 0 && rdev->supply)
+               regulator_disable(rdev->supply);
 
        return ret;
 }
 EXPORT_SYMBOL_GPL(regulator_disable);
 
 /* locks held by regulator_force_disable() */
-static int _regulator_force_disable(struct regulator_dev *rdev,
-               struct regulator_dev **supply_rdev_ptr)
+static int _regulator_force_disable(struct regulator_dev *rdev)
 {
        int ret = 0;
 
@@ -1497,10 +1522,6 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
                        REGULATOR_EVENT_DISABLE, NULL);
        }
 
-       /* decrease our supplies ref count and disable if required */
-       *supply_rdev_ptr = rdev->supply;
-
-       rdev->use_count = 0;
        return ret;
 }
 
@@ -1516,16 +1537,16 @@ static int _regulator_force_disable(struct regulator_dev *rdev,
 int regulator_force_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       struct regulator_dev *supply_rdev = NULL;
        int ret;
 
        mutex_lock(&rdev->mutex);
        regulator->uA_load = 0;
-       ret = _regulator_force_disable(rdev, &supply_rdev);
+       ret = _regulator_force_disable(regulator->rdev);
        mutex_unlock(&rdev->mutex);
 
-       if (supply_rdev)
-               regulator_disable(get_device_regulator(rdev_get_dev(supply_rdev)));
+       if (rdev->supply)
+               while (rdev->open_count--)
+                       regulator_disable(rdev->supply);
 
        return ret;
 }
@@ -2136,7 +2157,7 @@ int regulator_set_optimum_mode(struct regulator *regulator, int uA_load)
        /* get input voltage */
        input_uV = 0;
        if (rdev->supply)
-               input_uV = _regulator_get_voltage(rdev->supply);
+               input_uV = regulator_get_voltage(rdev->supply);
        if (input_uV <= 0)
                input_uV = rdev->constraints->input_uV;
        if (input_uV <= 0) {
@@ -2206,17 +2227,8 @@ EXPORT_SYMBOL_GPL(regulator_unregister_notifier);
 static void _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data)
 {
-       struct regulator_dev *_rdev;
-
        /* call rdev chain first */
        blocking_notifier_call_chain(&rdev->notifier, event, NULL);
-
-       /* now notify regulator we supply */
-       list_for_each_entry(_rdev, &rdev->supply_list, slist) {
-               mutex_lock(&_rdev->mutex);
-               _notifier_call_chain(_rdev, event, data);
-               mutex_unlock(&_rdev->mutex);
-       }
 }
 
 /**
@@ -2264,6 +2276,13 @@ err:
 }
 EXPORT_SYMBOL_GPL(regulator_bulk_get);
 
+static void regulator_bulk_enable_async(void *data, async_cookie_t cookie)
+{
+       struct regulator_bulk_data *bulk = data;
+
+       bulk->ret = regulator_enable(bulk->consumer);
+}
+
 /**
  * regulator_bulk_enable - enable multiple regulator consumers
  *
@@ -2279,21 +2298,33 @@ EXPORT_SYMBOL_GPL(regulator_bulk_get);
 int regulator_bulk_enable(int num_consumers,
                          struct regulator_bulk_data *consumers)
 {
+       LIST_HEAD(async_domain);
        int i;
-       int ret;
+       int ret = 0;
+
+       for (i = 0; i < num_consumers; i++)
+               async_schedule_domain(regulator_bulk_enable_async,
+                                     &consumers[i], &async_domain);
+
+       async_synchronize_full_domain(&async_domain);
 
+       /* If any consumer failed we need to unwind any that succeeded */
        for (i = 0; i < num_consumers; i++) {
-               ret = regulator_enable(consumers[i].consumer);
-               if (ret != 0)
+               if (consumers[i].ret != 0) {
+                       ret = consumers[i].ret;
                        goto err;
+               }
        }
 
        return 0;
 
 err:
-       pr_err("Failed to enable %s: %d\n", consumers[i].supply, ret);
-       for (--i; i >= 0; --i)
-               regulator_disable(consumers[i].consumer);
+       for (i = 0; i < num_consumers; i++)
+               if (consumers[i].ret == 0)
+                       regulator_disable(consumers[i].consumer);
+               else
+                       pr_err("Failed to enable %s: %d\n",
+                              consumers[i].supply, consumers[i].ret);
 
        return ret;
 }
@@ -2589,9 +2620,7 @@ struct regulator_dev *regulator_register(struct regulator_desc *regulator_desc,
        rdev->owner = regulator_desc->owner;
        rdev->desc = regulator_desc;
        INIT_LIST_HEAD(&rdev->consumer_list);
-       INIT_LIST_HEAD(&rdev->supply_list);
        INIT_LIST_HEAD(&rdev->list);
-       INIT_LIST_HEAD(&rdev->slist);
        BLOCKING_INIT_NOTIFIER_HEAD(&rdev->notifier);
 
        /* preform any regulator specific init */
@@ -2672,6 +2701,7 @@ unset_supplies:
        unset_regulator_supplies(rdev);
 
 scrub:
+       kfree(rdev->constraints);
        device_unregister(&rdev->dev);
        /* device core frees rdev */
        rdev = ERR_PTR(ret);
@@ -2703,7 +2733,7 @@ void regulator_unregister(struct regulator_dev *rdev)
        unset_regulator_supplies(rdev);
        list_del(&rdev->list);
        if (rdev->supply)
-               sysfs_remove_link(&rdev->dev.kobj, "supply");
+               regulator_put(rdev->supply);
        device_unregister(&rdev->dev);
        kfree(rdev->constraints);
        mutex_unlock(&regulator_list_mutex);
index c7410bde7b5d92ec128bbb79850c62e0e9495d87..f6ef6694ab9896687074c9224e4e60fea919acbc 100644 (file)
@@ -36,6 +36,29 @@ static struct regulator_desc dummy_desc = {
        .ops = &dummy_ops,
 };
 
+static int __devinit dummy_regulator_probe(struct platform_device *pdev)
+{
+       int ret;
+
+       dummy_regulator_rdev = regulator_register(&dummy_desc, NULL,
+                                                 &dummy_initdata, NULL);
+       if (IS_ERR(dummy_regulator_rdev)) {
+               ret = PTR_ERR(dummy_regulator_rdev);
+               pr_err("Failed to register regulator: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct platform_driver dummy_regulator_driver = {
+       .probe          = dummy_regulator_probe,
+       .driver         = {
+               .name           = "reg-dummy",
+               .owner          = THIS_MODULE,
+       },
+};
+
 static struct platform_device *dummy_pdev;
 
 void __init regulator_dummy_init(void)
@@ -55,12 +78,9 @@ void __init regulator_dummy_init(void)
                return;
        }
 
-       dummy_regulator_rdev = regulator_register(&dummy_desc, NULL,
-                                                 &dummy_initdata, NULL);
-       if (IS_ERR(dummy_regulator_rdev)) {
-               ret = PTR_ERR(dummy_regulator_rdev);
-               pr_err("Failed to register regulator: %d\n", ret);
+       ret = platform_driver_register(&dummy_regulator_driver);
+       if (ret != 0) {
+               pr_err("Failed to register dummy regulator driver: %d\n", ret);
                platform_device_unregister(dummy_pdev);
-               return;
        }
 }
index 55dd4e6650db80caa02d42a61f7315b90473b809..66d2d60b436a3e7a28092d64a499fd203db764a5 100644 (file)
@@ -49,7 +49,6 @@
 #define TPS65911_REG_LDO7              11
 #define TPS65911_REG_LDO8              12
 
-#define TPS65910_NUM_REGULATOR         13
 #define TPS65910_SUPPLY_STATE_ENABLED  0x1
 
 /* supported VIO voltages in milivolts */
@@ -264,11 +263,12 @@ static struct tps_info tps65911_regs[] = {
 };
 
 struct tps65910_reg {
-       struct regulator_desc desc[TPS65910_NUM_REGULATOR];
+       struct regulator_desc *desc;
        struct tps65910 *mfd;
-       struct regulator_dev *rdev[TPS65910_NUM_REGULATOR];
-       struct tps_info *info[TPS65910_NUM_REGULATOR];
+       struct regulator_dev **rdev;
+       struct tps_info **info;
        struct mutex mutex;
+       int num_regulators;
        int mode;
        int  (*get_ctrl_reg)(int);
 };
@@ -759,8 +759,13 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev,
                mult = (selector / VDD1_2_NUM_VOLTS) + 1;
                volt = VDD1_2_MIN_VOLT +
                                (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET;
+               break;
        case TPS65911_REG_VDDCTRL:
                volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
+               break;
+       default:
+               BUG();
+               return -EINVAL;
        }
 
        return  volt * 100 * mult;
@@ -897,16 +902,42 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
        switch(tps65910_chip_id(tps65910)) {
        case TPS65910:
                pmic->get_ctrl_reg = &tps65910_get_ctrl_register;
+               pmic->num_regulators = ARRAY_SIZE(tps65910_regs);
                info = tps65910_regs;
+               break;
        case TPS65911:
                pmic->get_ctrl_reg = &tps65911_get_ctrl_register;
+               pmic->num_regulators = ARRAY_SIZE(tps65911_regs);
                info = tps65911_regs;
+               break;
        default:
                pr_err("Invalid tps chip version\n");
+               kfree(pmic);
                return -ENODEV;
        }
 
-       for (i = 0; i < TPS65910_NUM_REGULATOR; i++, info++, reg_data++) {
+       pmic->desc = kcalloc(pmic->num_regulators,
+                       sizeof(struct regulator_desc), GFP_KERNEL);
+       if (!pmic->desc) {
+               err = -ENOMEM;
+               goto err_free_pmic;
+       }
+
+       pmic->info = kcalloc(pmic->num_regulators,
+                       sizeof(struct tps_info *), GFP_KERNEL);
+       if (!pmic->info) {
+               err = -ENOMEM;
+               goto err_free_desc;
+       }
+
+       pmic->rdev = kcalloc(pmic->num_regulators,
+                       sizeof(struct regulator_dev *), GFP_KERNEL);
+       if (!pmic->rdev) {
+               err = -ENOMEM;
+               goto err_free_info;
+       }
+
+       for (i = 0; i < pmic->num_regulators; i++, info++, reg_data++) {
                /* Register the regulators */
                pmic->info[i] = info;
 
@@ -938,7 +969,7 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
                                "failed to register %s regulator\n",
                                pdev->name);
                        err = PTR_ERR(rdev);
-                       goto err;
+                       goto err_unregister_regulator;
                }
 
                /* Save regulator for cleanup */
@@ -946,23 +977,31 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
        }
        return 0;
 
-err:
+err_unregister_regulator:
        while (--i >= 0)
                regulator_unregister(pmic->rdev[i]);
-
+       kfree(pmic->rdev);
+err_free_info:
+       kfree(pmic->info);
+err_free_desc:
+       kfree(pmic->desc);
+err_free_pmic:
        kfree(pmic);
        return err;
 }
 
 static int __devexit tps65910_remove(struct platform_device *pdev)
 {
-       struct tps65910_reg *tps65910_reg = platform_get_drvdata(pdev);
+       struct tps65910_reg *pmic = platform_get_drvdata(pdev);
        int i;
 
-       for (i = 0; i < TPS65910_NUM_REGULATOR; i++)
-               regulator_unregister(tps65910_reg->rdev[i]);
+       for (i = 0; i < pmic->num_regulators; i++)
+               regulator_unregister(pmic->rdev[i]);
 
-       kfree(tps65910_reg);
+       kfree(pmic->rdev);
+       kfree(pmic->info);
+       kfree(pmic->desc);
+       kfree(pmic);
        return 0;
 }
 
index 87fe0f75a56eed01375e0e2a393dbea6c1cd98b2..ee8747f4fa08b187ef2f79dbb51e3f69a770bc21 100644 (file)
@@ -835,8 +835,8 @@ static struct regulator_ops twlsmps_ops = {
                        remap_conf) \
                TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
                        remap_conf, TWL4030, twl4030fixed_ops)
-#define TWL6030_FIXED_LDO(label, offset, mVolts, num, turnon_delay) \
-               TWL_FIXED_LDO(label, offset, mVolts, num, turnon_delay, \
+#define TWL6030_FIXED_LDO(label, offset, mVolts, turnon_delay) \
+               TWL_FIXED_LDO(label, offset, mVolts, 0x0, turnon_delay, \
                        0x0, TWL6030, twl6030fixed_ops)
 
 #define TWL4030_ADJUSTABLE_LDO(label, offset, num, turnon_delay, remap_conf) { \
@@ -856,24 +856,22 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6030_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
                .name = #label, \
                .id = TWL6030_REG_##label, \
-               .n_voltages = (max_mVolts - min_mVolts)/100, \
+               .n_voltages = (max_mVolts - min_mVolts)/100 + 1, \
                .ops = &twl6030ldo_ops, \
                .type = REGULATOR_VOLTAGE, \
                .owner = THIS_MODULE, \
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts, num) { \
+#define TWL6025_ADJUSTABLE_LDO(label, offset, min_mVolts, max_mVolts) { \
        .base = offset, \
-       .id = num, \
        .min_mV = min_mVolts, \
        .max_mV = max_mVolts, \
        .desc = { \
@@ -903,9 +901,8 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6030_FIXED_RESOURCE(label, offset, num, turnon_delay) { \
+#define TWL6030_FIXED_RESOURCE(label, offset, turnon_delay) { \
        .base = offset, \
-       .id = num, \
        .delay = turnon_delay, \
        .desc = { \
                .name = #label, \
@@ -916,9 +913,8 @@ static struct regulator_ops twlsmps_ops = {
                }, \
        }
 
-#define TWL6025_ADJUSTABLE_SMPS(label, offset, num) { \
+#define TWL6025_ADJUSTABLE_SMPS(label, offset) { \
        .base = offset, \
-       .id = num, \
        .min_mV = 600, \
        .max_mV = 2100, \
        .desc = { \
@@ -961,32 +957,32 @@ static struct twlreg_info twl_regs[] = {
        /* 6030 REG with base as PMC Slave Misc : 0x0030 */
        /* Turnon-delay and remap configuration values for 6030 are not
           verified since the specification is not public */
-       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300, 1),
-       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300, 2),
-       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300, 3),
-       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300, 4),
-       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300, 5),
-       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300, 7),
-       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 15, 0),
-       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 16, 0),
-       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 17, 0),
-       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 18, 0),
-       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 48, 0),
+       TWL6030_ADJUSTABLE_LDO(VAUX1_6030, 0x54, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX2_6030, 0x58, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VAUX3_6030, 0x5c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VMMC, 0x68, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VPP, 0x6c, 1000, 3300),
+       TWL6030_ADJUSTABLE_LDO(VUSIM, 0x74, 1000, 3300),
+       TWL6030_FIXED_LDO(VANA, 0x50, 2100, 0),
+       TWL6030_FIXED_LDO(VCXIO, 0x60, 1800, 0),
+       TWL6030_FIXED_LDO(VDAC, 0x64, 1800, 0),
+       TWL6030_FIXED_LDO(VUSB, 0x70, 3300, 0),
+       TWL6030_FIXED_RESOURCE(CLK32KG, 0x8C, 0),
 
        /* 6025 are renamed compared to 6030 versions */
-       TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300, 1),
-       TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300, 2),
-       TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300, 3),
-       TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300, 4),
-       TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300, 5),
-       TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300, 7),
-       TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300, 16),
-       TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300, 17),
-       TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300, 18),
-
-       TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34, 1),
-       TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10, 2),
-       TWL6025_ADJUSTABLE_SMPS(VIO, 0x16, 3),
+       TWL6025_ADJUSTABLE_LDO(LDO2, 0x54, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO4, 0x58, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO3, 0x5c, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO5, 0x68, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO1, 0x6c, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO7, 0x74, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDO6, 0x60, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDOLN, 0x64, 1000, 3300),
+       TWL6025_ADJUSTABLE_LDO(LDOUSB, 0x70, 1000, 3300),
+
+       TWL6025_ADJUSTABLE_SMPS(SMPS3, 0x34),
+       TWL6025_ADJUSTABLE_SMPS(SMPS4, 0x10),
+       TWL6025_ADJUSTABLE_SMPS(VIO, 0x16),
 };
 
 static u8 twl_get_smps_offset(void)
index a0982e80985198004ae990218c38718d8616eec6..bd3531d8b2ac9364fb48c182c4947a00696d7c34 100644 (file)
@@ -267,23 +267,6 @@ static int wm831x_buckv_select_min_voltage(struct regulator_dev *rdev,
        return vsel;
 }
 
-static int wm831x_buckv_select_max_voltage(struct regulator_dev *rdev,
-                                          int min_uV, int max_uV)
-{
-       u16 vsel;
-
-       if (max_uV < 600000 || max_uV > 1800000)
-               return -EINVAL;
-
-       vsel = ((max_uV - 600000) / 12500) + 8;
-
-       if (wm831x_buckv_list_voltage(rdev, vsel) < min_uV ||
-           wm831x_buckv_list_voltage(rdev, vsel) < max_uV)
-               return -EINVAL;
-
-       return vsel;
-}
-
 static int wm831x_buckv_set_dvs(struct regulator_dev *rdev, int state)
 {
        struct wm831x_dcdc *dcdc = rdev_get_drvdata(rdev);
@@ -338,28 +321,23 @@ static int wm831x_buckv_set_voltage(struct regulator_dev *rdev,
        if (ret < 0)
                return ret;
 
-       /* Set the high voltage as the DVS voltage.  This is optimised
-        * for CPUfreq usage, most processors will keep the maximum
-        * voltage constant and lower the minimum with the frequency. */
-       vsel = wm831x_buckv_select_max_voltage(rdev, min_uV, max_uV);
-       if (vsel < 0) {
-               /* This should never happen - at worst the same vsel
-                * should be chosen */
-               WARN_ON(vsel < 0);
-               return 0;
+       /*
+        * If this VSEL is higher than the last one we've seen then
+        * remember it as the DVS VSEL.  This is optimised for CPUfreq
+        * usage where we want to get to the highest voltage very
+        * quickly.
+        */
+       if (vsel > dcdc->dvs_vsel) {
+               ret = wm831x_set_bits(wm831x, dvs_reg,
+                                     WM831X_DC1_DVS_VSEL_MASK,
+                                     dcdc->dvs_vsel);
+               if (ret == 0)
+                       dcdc->dvs_vsel = vsel;
+               else
+                       dev_warn(wm831x->dev,
+                                "Failed to set DCDC DVS VSEL: %d\n", ret);
        }
 
-       /* Don't bother if it's the same VSEL we're already using */
-       if (vsel == dcdc->on_vsel)
-               return 0;
-
-       ret = wm831x_set_bits(wm831x, dvs_reg, WM831X_DC1_DVS_VSEL_MASK, vsel);
-       if (ret == 0)
-               dcdc->dvs_vsel = vsel;
-       else
-               dev_warn(wm831x->dev, "Failed to set DCDC DVS VSEL: %d\n",
-                        ret);
-
        return 0;
 }
 
@@ -456,27 +434,6 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
        if (!pdata || !pdata->dvs_gpio)
                return;
 
-       switch (pdata->dvs_control_src) {
-       case 1:
-               ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
-               break;
-       case 2:
-               ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
-               break;
-       default:
-               dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
-                       pdata->dvs_control_src, dcdc->name);
-               return;
-       }
-
-       ret = wm831x_set_bits(wm831x, dcdc->base + WM831X_DCDC_DVS_CONTROL,
-                             WM831X_DC1_DVS_SRC_MASK, ctrl);
-       if (ret < 0) {
-               dev_err(wm831x->dev, "Failed to set %s DVS source: %d\n",
-                       dcdc->name, ret);
-               return;
-       }
-
        ret = gpio_request(pdata->dvs_gpio, "DCDC DVS");
        if (ret < 0) {
                dev_err(wm831x->dev, "Failed to get %s DVS GPIO: %d\n",
@@ -498,17 +455,57 @@ static __devinit void wm831x_buckv_dvs_init(struct wm831x_dcdc *dcdc,
        }
 
        dcdc->dvs_gpio = pdata->dvs_gpio;
+
+       switch (pdata->dvs_control_src) {
+       case 1:
+               ctrl = 2 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       case 2:
+               ctrl = 3 << WM831X_DC1_DVS_SRC_SHIFT;
+               break;
+       default:
+               dev_err(wm831x->dev, "Invalid DVS control source %d for %s\n",
+                       pdata->dvs_control_src, dcdc->name);
+               return;
+       }
+
+       /* If DVS_VSEL is set to the minimum value then raise it to ON_VSEL
+        * to make bootstrapping a bit smoother.
+        */
+       if (!dcdc->dvs_vsel) {
+               ret = wm831x_set_bits(wm831x,
+                                     dcdc->base + WM831X_DCDC_DVS_CONTROL,
+                                     WM831X_DC1_DVS_VSEL_MASK, dcdc->on_vsel);
+               if (ret == 0)
+                       dcdc->dvs_vsel = dcdc->on_vsel;
+               else
+                       dev_warn(wm831x->dev, "Failed to set DVS_VSEL: %d\n",
+