Merge branch 'common/mmcif' into rmobile-latest
This commit is contained in:
commit
c488a4731a
181 changed files with 9949 additions and 2141 deletions
|
@ -385,6 +385,10 @@ mapped_file - # of bytes of mapped file (includes tmpfs/shmem)
|
||||||
pgpgin - # of pages paged in (equivalent to # of charging events).
|
pgpgin - # of pages paged in (equivalent to # of charging events).
|
||||||
pgpgout - # of pages paged out (equivalent to # of uncharging events).
|
pgpgout - # of pages paged out (equivalent to # of uncharging events).
|
||||||
swap - # of bytes of swap usage
|
swap - # of bytes of swap usage
|
||||||
|
dirty - # of bytes that are waiting to get written back to the disk.
|
||||||
|
writeback - # of bytes that are actively being written back to the disk.
|
||||||
|
nfs_unstable - # of bytes sent to the NFS server, but not yet committed to
|
||||||
|
the actual storage.
|
||||||
inactive_anon - # of bytes of anonymous memory and swap cache memory on
|
inactive_anon - # of bytes of anonymous memory and swap cache memory on
|
||||||
LRU list.
|
LRU list.
|
||||||
active_anon - # of bytes of anonymous and swap cache memory on active
|
active_anon - # of bytes of anonymous and swap cache memory on active
|
||||||
|
@ -406,6 +410,9 @@ total_mapped_file - sum of all children's "cache"
|
||||||
total_pgpgin - sum of all children's "pgpgin"
|
total_pgpgin - sum of all children's "pgpgin"
|
||||||
total_pgpgout - sum of all children's "pgpgout"
|
total_pgpgout - sum of all children's "pgpgout"
|
||||||
total_swap - sum of all children's "swap"
|
total_swap - sum of all children's "swap"
|
||||||
|
total_dirty - sum of all children's "dirty"
|
||||||
|
total_writeback - sum of all children's "writeback"
|
||||||
|
total_nfs_unstable - sum of all children's "nfs_unstable"
|
||||||
total_inactive_anon - sum of all children's "inactive_anon"
|
total_inactive_anon - sum of all children's "inactive_anon"
|
||||||
total_active_anon - sum of all children's "active_anon"
|
total_active_anon - sum of all children's "active_anon"
|
||||||
total_inactive_file - sum of all children's "inactive_file"
|
total_inactive_file - sum of all children's "inactive_file"
|
||||||
|
@ -453,6 +460,73 @@ memory under it will be reclaimed.
|
||||||
You can reset failcnt by writing 0 to failcnt file.
|
You can reset failcnt by writing 0 to failcnt file.
|
||||||
# echo 0 > .../memory.failcnt
|
# echo 0 > .../memory.failcnt
|
||||||
|
|
||||||
|
5.5 dirty memory
|
||||||
|
|
||||||
|
Control the maximum amount of dirty pages a cgroup can have at any given time.
|
||||||
|
|
||||||
|
Limiting dirty memory is like fixing the max amount of dirty (hard to reclaim)
|
||||||
|
page cache used by a cgroup. So, in case of multiple cgroup writers, they will
|
||||||
|
not be able to consume more than their designated share of dirty pages and will
|
||||||
|
be forced to perform write-out if they cross that limit.
|
||||||
|
|
||||||
|
The interface is equivalent to the procfs interface: /proc/sys/vm/dirty_*. It
|
||||||
|
is possible to configure a limit to trigger both a direct writeback or a
|
||||||
|
background writeback performed by per-bdi flusher threads. The root cgroup
|
||||||
|
memory.dirty_* control files are read-only and match the contents of
|
||||||
|
the /proc/sys/vm/dirty_* files.
|
||||||
|
|
||||||
|
Per-cgroup dirty limits can be set using the following files in the cgroupfs:
|
||||||
|
|
||||||
|
- memory.dirty_ratio: the amount of dirty memory (expressed as a percentage of
|
||||||
|
cgroup memory) at which a process generating dirty pages will itself start
|
||||||
|
writing out dirty data.
|
||||||
|
|
||||||
|
- memory.dirty_limit_in_bytes: the amount of dirty memory (expressed in bytes)
|
||||||
|
in the cgroup at which a process generating dirty pages will start itself
|
||||||
|
writing out dirty data. Suffix (k, K, m, M, g, or G) can be used to indicate
|
||||||
|
that value is kilo, mega or gigabytes.
|
||||||
|
|
||||||
|
Note: memory.dirty_limit_in_bytes is the counterpart of memory.dirty_ratio.
|
||||||
|
Only one of them may be specified at a time. When one is written it is
|
||||||
|
immediately taken into account to evaluate the dirty memory limits and the
|
||||||
|
other appears as 0 when read.
|
||||||
|
|
||||||
|
- memory.dirty_background_ratio: the amount of dirty memory of the cgroup
|
||||||
|
(expressed as a percentage of cgroup memory) at which background writeback
|
||||||
|
kernel threads will start writing out dirty data.
|
||||||
|
|
||||||
|
- memory.dirty_background_limit_in_bytes: the amount of dirty memory (expressed
|
||||||
|
in bytes) in the cgroup at which background writeback kernel threads will
|
||||||
|
start writing out dirty data. Suffix (k, K, m, M, g, or G) can be used to
|
||||||
|
indicate that value is kilo, mega or gigabytes.
|
||||||
|
|
||||||
|
Note: memory.dirty_background_limit_in_bytes is the counterpart of
|
||||||
|
memory.dirty_background_ratio. Only one of them may be specified at a time.
|
||||||
|
When one is written it is immediately taken into account to evaluate the dirty
|
||||||
|
memory limits and the other appears as 0 when read.
|
||||||
|
|
||||||
|
A cgroup may contain more dirty memory than its dirty limit. This is possible
|
||||||
|
because of the principle that the first cgroup to touch a page is charged for
|
||||||
|
it. Subsequent page counting events (dirty, writeback, nfs_unstable) are also
|
||||||
|
counted to the originally charged cgroup.
|
||||||
|
|
||||||
|
Example: If page is allocated by a cgroup A task, then the page is charged to
|
||||||
|
cgroup A. If the page is later dirtied by a task in cgroup B, then the cgroup A
|
||||||
|
dirty count will be incremented. If cgroup A is over its dirty limit but cgroup
|
||||||
|
B is not, then dirtying a cgroup A page from a cgroup B task may push cgroup A
|
||||||
|
over its dirty limit without throttling the dirtying cgroup B task.
|
||||||
|
|
||||||
|
When use_hierarchy=0, each cgroup has dirty memory usage and limits.
|
||||||
|
System-wide dirty limits are also consulted. Dirty memory consumption is
|
||||||
|
checked against both system-wide and per-cgroup dirty limits.
|
||||||
|
|
||||||
|
The current implementation does not enforce per-cgroup dirty limits when
|
||||||
|
use_hierarchy=1. System-wide dirty limits are used for processes in such
|
||||||
|
cgroups. Attempts to read memory.dirty_* files return the system-wide
|
||||||
|
values. Writes to the memory.dirty_* files return error. An enhanced
|
||||||
|
implementation is needed to check the chain of parents to ensure that no
|
||||||
|
dirty limit is exceeded.
|
||||||
|
|
||||||
6. Hierarchy support
|
6. Hierarchy support
|
||||||
|
|
||||||
The memory controller supports a deep hierarchy and hierarchical accounting.
|
The memory controller supports a deep hierarchy and hierarchical accounting.
|
||||||
|
|
|
@ -8,7 +8,7 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
|
||||||
|
|
||||||
<cipher>
|
<cipher>
|
||||||
Encryption cipher and an optional IV generation mode.
|
Encryption cipher and an optional IV generation mode.
|
||||||
(In format cipher-chainmode-ivopts:ivmode).
|
(In format cipher[:keycount]-chainmode-ivopts:ivmode).
|
||||||
Examples:
|
Examples:
|
||||||
des
|
des
|
||||||
aes-cbc-essiv:sha256
|
aes-cbc-essiv:sha256
|
||||||
|
@ -20,6 +20,11 @@ Parameters: <cipher> <key> <iv_offset> <device path> <offset>
|
||||||
Key used for encryption. It is encoded as a hexadecimal number.
|
Key used for encryption. It is encoded as a hexadecimal number.
|
||||||
You can only use key sizes that are valid for the selected cipher.
|
You can only use key sizes that are valid for the selected cipher.
|
||||||
|
|
||||||
|
<keycount>
|
||||||
|
Multi-key compatibility mode. You can define <keycount> keys and
|
||||||
|
then sectors are encrypted according to their offsets (sector 0 uses key0;
|
||||||
|
sector 1 uses key1 etc.). <keycount> must be a power of two.
|
||||||
|
|
||||||
<iv_offset>
|
<iv_offset>
|
||||||
The IV offset is a sector count that is added to the sector number
|
The IV offset is a sector count that is added to the sector number
|
||||||
before creating the IV.
|
before creating the IV.
|
||||||
|
|
70
Documentation/device-mapper/dm-raid.txt
Normal file
70
Documentation/device-mapper/dm-raid.txt
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
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.
|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|
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
|
||||||
|
# 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)
|
||||||
|
# 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
|
||||||
|
|
||||||
|
Performing a 'dmsetup table' should display the CTR table used to
|
||||||
|
construct the mapping (with possible reordering of optional
|
||||||
|
parameters).
|
||||||
|
|
||||||
|
Performing a 'dmsetup status' will yield 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:
|
||||||
|
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.
|
|
@ -375,6 +375,7 @@ Anonymous: 0 kB
|
||||||
Swap: 0 kB
|
Swap: 0 kB
|
||||||
KernelPageSize: 4 kB
|
KernelPageSize: 4 kB
|
||||||
MMUPageSize: 4 kB
|
MMUPageSize: 4 kB
|
||||||
|
Locked: 374 kB
|
||||||
|
|
||||||
The first of these lines shows the same information as is displayed for the
|
The first of these lines shows the same information as is displayed for the
|
||||||
mapping in /proc/PID/maps. The remaining lines show the size of the mapping
|
mapping in /proc/PID/maps. The remaining lines show the size of the mapping
|
||||||
|
@ -670,6 +671,8 @@ varies by architecture and compile options. The following is from a
|
||||||
|
|
||||||
> cat /proc/meminfo
|
> cat /proc/meminfo
|
||||||
|
|
||||||
|
The "Locked" indicates whether the mapping is locked in memory or not.
|
||||||
|
|
||||||
|
|
||||||
MemTotal: 16344972 kB
|
MemTotal: 16344972 kB
|
||||||
MemFree: 13634064 kB
|
MemFree: 13634064 kB
|
||||||
|
@ -1320,6 +1323,10 @@ scaled linearly with /proc/<pid>/oom_score_adj.
|
||||||
Writing to /proc/<pid>/oom_score_adj or /proc/<pid>/oom_adj will change the
|
Writing to /proc/<pid>/oom_score_adj or /proc/<pid>/oom_adj will change the
|
||||||
other with its scaled value.
|
other with its scaled value.
|
||||||
|
|
||||||
|
The value of /proc/<pid>/oom_score_adj may be reduced no lower than the last
|
||||||
|
value set by a CAP_SYS_RESOURCE process. To reduce the value any lower
|
||||||
|
requires CAP_SYS_RESOURCE.
|
||||||
|
|
||||||
NOTICE: /proc/<pid>/oom_adj is deprecated and will be removed, please see
|
NOTICE: /proc/<pid>/oom_adj is deprecated and will be removed, please see
|
||||||
Documentation/feature-removal-schedule.txt.
|
Documentation/feature-removal-schedule.txt.
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ setting up a platform_device using the GPIO, is mark its direction:
|
||||||
int gpio_direction_input(unsigned gpio);
|
int gpio_direction_input(unsigned gpio);
|
||||||
int gpio_direction_output(unsigned gpio, int value);
|
int gpio_direction_output(unsigned gpio, int value);
|
||||||
|
|
||||||
The return value is zero for success, else a negative errno. It must
|
The return value is zero for success, else a negative errno. It should
|
||||||
be checked, since the get/set calls don't have error returns and since
|
be checked, since the get/set calls don't have error returns and since
|
||||||
misconfiguration is possible. You should normally issue these calls from
|
misconfiguration is possible. You should normally issue these calls from
|
||||||
a task context. However, for spinlock-safe GPIOs it's OK to use them
|
a task context. However, for spinlock-safe GPIOs it's OK to use them
|
||||||
|
|
298
Documentation/vm/transhuge.txt
Normal file
298
Documentation/vm/transhuge.txt
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
= Transparent Hugepage Support =
|
||||||
|
|
||||||
|
== Objective ==
|
||||||
|
|
||||||
|
Performance critical computing applications dealing with large memory
|
||||||
|
working sets are already running on top of libhugetlbfs and in turn
|
||||||
|
hugetlbfs. Transparent Hugepage Support is an alternative means of
|
||||||
|
using huge pages for the backing of virtual memory with huge pages
|
||||||
|
that supports the automatic promotion and demotion of page sizes and
|
||||||
|
without the shortcomings of hugetlbfs.
|
||||||
|
|
||||||
|
Currently it only works for anonymous memory mappings but in the
|
||||||
|
future it can expand over the pagecache layer starting with tmpfs.
|
||||||
|
|
||||||
|
The reason applications are running faster is because of two
|
||||||
|
factors. The first factor is almost completely irrelevant and it's not
|
||||||
|
of significant interest because it'll also have the downside of
|
||||||
|
requiring larger clear-page copy-page in page faults which is a
|
||||||
|
potentially negative effect. The first factor consists in taking a
|
||||||
|
single page fault for each 2M virtual region touched by userland (so
|
||||||
|
reducing the enter/exit kernel frequency by a 512 times factor). This
|
||||||
|
only matters the first time the memory is accessed for the lifetime of
|
||||||
|
a memory mapping. The second long lasting and much more important
|
||||||
|
factor will affect all subsequent accesses to the memory for the whole
|
||||||
|
runtime of the application. The second factor consist of two
|
||||||
|
components: 1) the TLB miss will run faster (especially with
|
||||||
|
virtualization using nested pagetables but almost always also on bare
|
||||||
|
metal without virtualization) and 2) a single TLB entry will be
|
||||||
|
mapping a much larger amount of virtual memory in turn reducing the
|
||||||
|
number of TLB misses. With virtualization and nested pagetables the
|
||||||
|
TLB can be mapped of larger size only if both KVM and the Linux guest
|
||||||
|
are using hugepages but a significant speedup already happens if only
|
||||||
|
one of the two is using hugepages just because of the fact the TLB
|
||||||
|
miss is going to run faster.
|
||||||
|
|
||||||
|
== Design ==
|
||||||
|
|
||||||
|
- "graceful fallback": mm components which don't have transparent
|
||||||
|
hugepage knowledge fall back to breaking a transparent hugepage and
|
||||||
|
working on the regular pages and their respective regular pmd/pte
|
||||||
|
mappings
|
||||||
|
|
||||||
|
- if a hugepage allocation fails because of memory fragmentation,
|
||||||
|
regular pages should be gracefully allocated instead and mixed in
|
||||||
|
the same vma without any failure or significant delay and without
|
||||||
|
userland noticing
|
||||||
|
|
||||||
|
- if some task quits and more hugepages become available (either
|
||||||
|
immediately in the buddy or through the VM), guest physical memory
|
||||||
|
backed by regular pages should be relocated on hugepages
|
||||||
|
automatically (with khugepaged)
|
||||||
|
|
||||||
|
- it doesn't require memory reservation and in turn it uses hugepages
|
||||||
|
whenever possible (the only possible reservation here is kernelcore=
|
||||||
|
to avoid unmovable pages to fragment all the memory but such a tweak
|
||||||
|
is not specific to transparent hugepage support and it's a generic
|
||||||
|
feature that applies to all dynamic high order allocations in the
|
||||||
|
kernel)
|
||||||
|
|
||||||
|
- this initial support only offers the feature in the anonymous memory
|
||||||
|
regions but it'd be ideal to move it to tmpfs and the pagecache
|
||||||
|
later
|
||||||
|
|
||||||
|
Transparent Hugepage Support maximizes the usefulness of free memory
|
||||||
|
if compared to the reservation approach of hugetlbfs by allowing all
|
||||||
|
unused memory to be used as cache or other movable (or even unmovable
|
||||||
|
entities). It doesn't require reservation to prevent hugepage
|
||||||
|
allocation failures to be noticeable from userland. It allows paging
|
||||||
|
and all other advanced VM features to be available on the
|
||||||
|
hugepages. It requires no modifications for applications to take
|
||||||
|
advantage of it.
|
||||||
|
|
||||||
|
Applications however can be further optimized to take advantage of
|
||||||
|
this feature, like for example they've been optimized before to avoid
|
||||||
|
a flood of mmap system calls for every malloc(4k). Optimizing userland
|
||||||
|
is by far not mandatory and khugepaged already can take care of long
|
||||||
|
lived page allocations even for hugepage unaware applications that
|
||||||
|
deals with large amounts of memory.
|
||||||
|
|
||||||
|
In certain cases when hugepages are enabled system wide, application
|
||||||
|
may end up allocating more memory resources. An application may mmap a
|
||||||
|
large region but only touch 1 byte of it, in that case a 2M page might
|
||||||
|
be allocated instead of a 4k page for no good. This is why it's
|
||||||
|
possible to disable hugepages system-wide and to only have them inside
|
||||||
|
MADV_HUGEPAGE madvise regions.
|
||||||
|
|
||||||
|
Embedded systems should enable hugepages only inside madvise regions
|
||||||
|
to eliminate any risk of wasting any precious byte of memory and to
|
||||||
|
only run faster.
|
||||||
|
|
||||||
|
Applications that gets a lot of benefit from hugepages and that don't
|
||||||
|
risk to lose memory by using hugepages, should use
|
||||||
|
madvise(MADV_HUGEPAGE) on their critical mmapped regions.
|
||||||
|
|
||||||
|
== sysfs ==
|
||||||
|
|
||||||
|
Transparent Hugepage Support can be entirely disabled (mostly for
|
||||||
|
debugging purposes) or only enabled inside MADV_HUGEPAGE regions (to
|
||||||
|
avoid the risk of consuming more memory resources) or enabled system
|
||||||
|
wide. This can be achieved with one of:
|
||||||
|
|
||||||
|
echo always >/sys/kernel/mm/transparent_hugepage/enabled
|
||||||
|
echo madvise >/sys/kernel/mm/transparent_hugepage/enabled
|
||||||
|
echo never >/sys/kernel/mm/transparent_hugepage/enabled
|
||||||
|
|
||||||
|
It's also possible to limit defrag efforts in the VM to generate
|
||||||
|
hugepages in case they're not immediately free to madvise regions or
|
||||||
|
to never try to defrag memory and simply fallback to regular pages
|
||||||
|
unless hugepages are immediately available. Clearly if we spend CPU
|
||||||
|
time to defrag memory, we would expect to gain even more by the fact
|
||||||
|
we use hugepages later instead of regular pages. This isn't always
|
||||||
|
guaranteed, but it may be more likely in case the allocation is for a
|
||||||
|
MADV_HUGEPAGE region.
|
||||||
|
|
||||||
|
echo always >/sys/kernel/mm/transparent_hugepage/defrag
|
||||||
|
echo madvise >/sys/kernel/mm/transparent_hugepage/defrag
|
||||||
|
echo never >/sys/kernel/mm/transparent_hugepage/defrag
|
||||||
|
|
||||||
|
khugepaged will be automatically started when
|
||||||
|
transparent_hugepage/enabled is set to "always" or "madvise, and it'll
|
||||||
|
be automatically shutdown if it's set to "never".
|
||||||
|
|
||||||
|
khugepaged runs usually at low frequency so while one may not want to
|
||||||
|
invoke defrag algorithms synchronously during the page faults, it
|
||||||
|
should be worth invoking defrag at least in khugepaged. However it's
|
||||||
|
also possible to disable defrag in khugepaged:
|
||||||
|
|
||||||
|
echo yes >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
|
||||||
|
echo no >/sys/kernel/mm/transparent_hugepage/khugepaged/defrag
|
||||||
|
|
||||||
|
You can also control how many pages khugepaged should scan at each
|
||||||
|
pass:
|
||||||
|
|
||||||
|
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_to_scan
|
||||||
|
|
||||||
|
and how many milliseconds to wait in khugepaged between each pass (you
|
||||||
|
can set this to 0 to run khugepaged at 100% utilization of one core):
|
||||||
|
|
||||||
|
/sys/kernel/mm/transparent_hugepage/khugepaged/scan_sleep_millisecs
|
||||||
|
|
||||||
|
and how many milliseconds to wait in khugepaged if there's an hugepage
|
||||||
|
allocation failure to throttle the next allocation attempt.
|
||||||
|
|
||||||
|
/sys/kernel/mm/transparent_hugepage/khugepaged/alloc_sleep_millisecs
|
||||||
|
|
||||||
|
The khugepaged progress can be seen in the number of pages collapsed:
|
||||||
|
|
||||||
|
/sys/kernel/mm/transparent_hugepage/khugepaged/pages_collapsed
|
||||||
|
|
||||||
|
for each pass:
|
||||||
|
|
||||||
|
/sys/kernel/mm/transparent_hugepage/khugepaged/full_scans
|
||||||
|
|
||||||
|
== Boot parameter ==
|
||||||
|
|
||||||
|
You can change the sysfs boot time defaults of Transparent Hugepage
|
||||||
|
Support by passing the parameter "transparent_hugepage=always" or
|
||||||
|
"transparent_hugepage=madvise" or "transparent_hugepage=never"
|
||||||
|
(without "") to the kernel command line.
|
||||||
|
|
||||||
|
== Need of application restart ==
|
||||||
|
|
||||||
|
The transparent_hugepage/enabled values only affect future
|
||||||
|
behavior. So to make them effective you need to restart any
|
||||||
|
application that could have been using hugepages. This also applies to
|
||||||
|
the regions registered in khugepaged.
|
||||||
|
|
||||||
|
== get_user_pages and follow_page ==
|
||||||
|
|
||||||
|
get_user_pages and follow_page if run on a hugepage, will return the
|
||||||
|
head or tail pages as usual (exactly as they would do on
|
||||||
|
hugetlbfs). Most gup users will only care about the actual physical
|
||||||
|
address of the page and its temporary pinning to release after the I/O
|
||||||
|
is complete, so they won't ever notice the fact the page is huge. But
|
||||||
|
if any driver is going to mangle over the page structure of the tail
|
||||||
|
page (like for checking page->mapping or other bits that are relevant
|
||||||
|
for the head page and not the tail page), it should be updated to jump
|
||||||
|
to check head page instead (while serializing properly against
|
||||||
|
split_huge_page() to avoid the head and tail pages to disappear from
|
||||||
|
under it, see the futex code to see an example of that, hugetlbfs also
|
||||||
|
needed special handling in futex code for similar reasons).
|
||||||
|
|
||||||
|
NOTE: these aren't new constraints to the GUP API, and they match the
|
||||||
|
same constrains that applies to hugetlbfs too, so any driver capable
|
||||||
|
of handling GUP on hugetlbfs will also work fine on transparent
|
||||||
|
hugepage backed mappings.
|
||||||
|
|
||||||
|
In case you can't handle compound pages if they're returned by
|
||||||
|
follow_page, the FOLL_SPLIT bit can be specified as parameter to
|
||||||
|
follow_page, so that it will split the hugepages before returning
|
||||||
|
them. Migration for example passes FOLL_SPLIT as parameter to
|
||||||
|
follow_page because it's not hugepage aware and in fact it can't work
|
||||||
|
at all on hugetlbfs (but it instead works fine on transparent
|
||||||
|
hugepages thanks to FOLL_SPLIT). migration simply can't deal with
|
||||||
|
hugepages being returned (as it's not only checking the pfn of the
|
||||||
|
page and pinning it during the copy but it pretends to migrate the
|
||||||
|
memory in regular page sizes and with regular pte/pmd mappings).
|
||||||
|
|
||||||
|
== Optimizing the applications ==
|
||||||
|
|
||||||
|
To be guaranteed that the kernel will map a 2M page immediately in any
|
||||||
|
memory region, the mmap region has to be hugepage naturally
|
||||||
|
aligned. posix_memalign() can provide that guarantee.
|
||||||
|
|
||||||
|
== Hugetlbfs ==
|
||||||
|
|
||||||
|
You can use hugetlbfs on a kernel that has transparent hugepage
|
||||||
|
support enabled just fine as always. No difference can be noted in
|
||||||
|
hugetlbfs other than there will be less overall fragmentation. All
|
||||||
|
usual features belonging to hugetlbfs are preserved and
|
||||||
|
unaffected. libhugetlbfs will also work fine as usual.
|
||||||
|
|
||||||
|
== Graceful fallback ==
|
||||||
|
|
||||||
|
Code walking pagetables but unware about huge pmds can simply call
|
||||||
|
split_huge_page_pmd(mm, pmd) where the pmd is the one returned by
|
||||||
|
pmd_offset. It's trivial to make the code transparent hugepage aware
|
||||||
|
by just grepping for "pmd_offset" and adding split_huge_page_pmd where
|
||||||
|
missing after pmd_offset returns the pmd. Thanks to the graceful
|
||||||
|
fallback design, with a one liner change, you can avoid to write
|
||||||
|
hundred if not thousand of lines of complex code to make your code
|
||||||
|
hugepage aware.
|
||||||
|
|
||||||
|
If you're not walking pagetables but you run into a physical hugepage
|
||||||
|
but you can't handle it natively in your code, you can split it by
|
||||||
|
calling split_huge_page(page). This is what the Linux VM does before
|
||||||
|
it tries to swapout the hugepage for example.
|
||||||
|
|
||||||
|
Example to make mremap.c transparent hugepage aware with a one liner
|
||||||
|
change:
|
||||||
|
|
||||||
|
diff --git a/mm/mremap.c b/mm/mremap.c
|
||||||
|
--- a/mm/mremap.c
|
||||||
|
+++ b/mm/mremap.c
|
||||||
|
@@ -41,6 +41,7 @@ static pmd_t *get_old_pmd(struct mm_stru
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
pmd = pmd_offset(pud, addr);
|
||||||
|
+ split_huge_page_pmd(mm, pmd);
|
||||||
|
if (pmd_none_or_clear_bad(pmd))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
== Locking in hugepage aware code ==
|
||||||
|
|
||||||
|
We want as much code as possible hugepage aware, as calling
|
||||||
|
split_huge_page() or split_huge_page_pmd() has a cost.
|
||||||
|
|
||||||
|
To make pagetable walks huge pmd aware, all you need to do is to call
|
||||||
|
pmd_trans_huge() on the pmd returned by pmd_offset. You must hold the
|
||||||
|
mmap_sem in read (or write) mode to be sure an huge pmd cannot be
|
||||||
|
created from under you by khugepaged (khugepaged collapse_huge_page
|
||||||
|
takes the mmap_sem in write mode in addition to the anon_vma lock). If
|
||||||
|
pmd_trans_huge returns false, you just fallback in the old code
|
||||||
|
paths. If instead pmd_trans_huge returns true, you have to take the
|
||||||
|
mm->page_table_lock and re-run pmd_trans_huge. Taking the
|
||||||
|
page_table_lock will prevent the huge pmd to be converted into a
|
||||||
|
regular pmd from under you (split_huge_page can run in parallel to the
|
||||||
|
pagetable walk). If the second pmd_trans_huge returns false, you
|
||||||
|
should just drop the page_table_lock and fallback to the old code as
|
||||||
|
before. Otherwise you should run pmd_trans_splitting on the pmd. In
|
||||||
|
case pmd_trans_splitting returns true, it means split_huge_page is
|
||||||
|
already in the middle of splitting the page. So if pmd_trans_splitting
|
||||||
|
returns true it's enough to drop the page_table_lock and call
|
||||||
|
wait_split_huge_page and then fallback the old code paths. You are
|
||||||
|
guaranteed by the time wait_split_huge_page returns, the pmd isn't
|
||||||
|
huge anymore. If pmd_trans_splitting returns false, you can proceed to
|
||||||
|
process the huge pmd and the hugepage natively. Once finished you can
|
||||||
|
drop the page_table_lock.
|
||||||
|
|
||||||
|
== compound_lock, get_user_pages and put_page ==
|
||||||
|
|
||||||
|
split_huge_page internally has to distribute the refcounts in the head
|
||||||
|
page to the tail pages before clearing all PG_head/tail bits from the
|
||||||
|
page structures. It can do that easily for refcounts taken by huge pmd
|
||||||
|
mappings. But the GUI API as created by hugetlbfs (that returns head
|
||||||
|
and tail pages if running get_user_pages on an address backed by any
|
||||||
|
hugepage), requires the refcount to be accounted on the tail pages and
|
||||||
|
not only in the head pages, if we want to be able to run
|
||||||
|
split_huge_page while there are gup pins established on any tail
|
||||||
|
page. Failure to be able to run split_huge_page if there's any gup pin
|
||||||
|
on any tail page, would mean having to split all hugepages upfront in
|
||||||
|
get_user_pages which is unacceptable as too many gup users are
|
||||||
|
performance critical and they must work natively on hugepages like
|
||||||
|
they work natively on hugetlbfs already (hugetlbfs is simpler because
|
||||||
|
hugetlbfs pages cannot be splitted so there wouldn't be requirement of
|
||||||
|
accounting the pins on the tail pages for hugetlbfs). If we wouldn't
|
||||||
|
account the gup refcounts on the tail pages during gup, we won't know
|
||||||
|
anymore which tail page is pinned by gup and which is not while we run
|
||||||
|
split_huge_page. But we still have to add the gup pin to the head page
|
||||||
|
too, to know when we can free the compound page in case it's never
|
||||||
|
splitted during its lifetime. That requires changing not just
|
||||||
|
get_page, but put_page as well so that when put_page runs on a tail
|
||||||
|
page (and only on a tail page) it will find its respective head page,
|
||||||
|
and then it will decrease the head page refcount in addition to the
|
||||||
|
tail page refcount. To obtain a head page reliably and to decrease its
|
||||||
|
refcount without race conditions, put_page has to serialize against
|
||||||
|
__split_huge_page_refcount using a special per-page lock called
|
||||||
|
compound_lock.
|
|
@ -6592,13 +6592,12 @@ F: Documentation/i2c/busses/i2c-viapro
|
||||||
F: drivers/i2c/busses/i2c-viapro.c
|
F: drivers/i2c/busses/i2c-viapro.c
|
||||||
|
|
||||||
VIA SD/MMC CARD CONTROLLER DRIVER
|
VIA SD/MMC CARD CONTROLLER DRIVER
|
||||||
M: Joseph Chan <JosephChan@via.com.tw>
|
M: Bruce Chang <brucechang@via.com.tw>
|
||||||
M: Harald Welte <HaraldWelte@viatech.com>
|
M: Harald Welte <HaraldWelte@viatech.com>
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/mmc/host/via-sdmmc.c
|
F: drivers/mmc/host/via-sdmmc.c
|
||||||
|
|
||||||
VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER
|
VIA UNICHROME(PRO)/CHROME9 FRAMEBUFFER DRIVER
|
||||||
M: Joseph Chan <JosephChan@via.com.tw>
|
|
||||||
M: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
|
M: Florian Tobias Schandinat <FlorianSchandinat@gmx.de>
|
||||||
L: linux-fbdev@vger.kernel.org
|
L: linux-fbdev@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
|
|
@ -53,6 +53,9 @@
|
||||||
#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
|
#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
|
||||||
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
||||||
|
|
||||||
|
#define MADV_HUGEPAGE 14 /* Worth backing with hugepages */
|
||||||
|
#define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */
|
||||||
|
|
||||||
/* compatibility flags */
|
/* compatibility flags */
|
||||||
#define MAP_FILE 0
|
#define MAP_FILE 0
|
||||||
|
|
||||||
|
|
|
@ -38,17 +38,9 @@
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
void *module_alloc(unsigned long size)
|
void *module_alloc(unsigned long size)
|
||||||
{
|
{
|
||||||
struct vm_struct *area;
|
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
|
||||||
|
GFP_KERNEL, PAGE_KERNEL_EXEC, -1,
|
||||||
size = PAGE_ALIGN(size);
|
__builtin_return_address(0));
|
||||||
if (!size)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
|
|
||||||
if (!area)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL_EXEC);
|
|
||||||
}
|
}
|
||||||
#else /* CONFIG_MMU */
|
#else /* CONFIG_MMU */
|
||||||
void *module_alloc(unsigned long size)
|
void *module_alloc(unsigned long size)
|
||||||
|
|
|
@ -50,7 +50,7 @@ pgd_t *pgd_alloc(struct mm_struct *mm)
|
||||||
if (!new_pmd)
|
if (!new_pmd)
|
||||||
goto no_pmd;
|
goto no_pmd;
|
||||||
|
|
||||||
new_pte = pte_alloc_map(mm, new_pmd, 0);
|
new_pte = pte_alloc_map(mm, NULL, new_pmd, 0);
|
||||||
if (!new_pte)
|
if (!new_pte)
|
||||||
goto no_pte;
|
goto no_pte;
|
||||||
|
|
||||||
|
|
|
@ -188,7 +188,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -203,7 +203,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -206,7 +206,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -150,7 +150,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
|
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -134,7 +134,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
|
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -162,7 +162,7 @@ static void __init set_hw_addr(struct platform_device *pdev)
|
||||||
*/
|
*/
|
||||||
regs = (void __iomem __force *)res->start;
|
regs = (void __iomem __force *)res->start;
|
||||||
pclk = clk_get(&pdev->dev, "pclk");
|
pclk = clk_get(&pdev->dev, "pclk");
|
||||||
if (!pclk)
|
if (IS_ERR(pclk))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
clk_enable(pclk);
|
clk_enable(pclk);
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -29,6 +26,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -72,8 +70,8 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_EEPROM_AT24=m
|
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
CONFIG_TUN=m
|
CONFIG_TUN=m
|
||||||
CONFIG_NET_ETHERNET=y
|
CONFIG_NET_ETHERNET=y
|
||||||
|
@ -106,6 +104,7 @@ CONFIG_GPIO_SYSFS=y
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
CONFIG_USB_GADGET=y
|
CONFIG_USB_GADGET=y
|
||||||
|
CONFIG_USB_GADGET_VBUS_DRAW=350
|
||||||
CONFIG_USB_ZERO=m
|
CONFIG_USB_ZERO=m
|
||||||
CONFIG_USB_ETH=m
|
CONFIG_USB_ETH=m
|
||||||
CONFIG_USB_GADGETFS=m
|
CONFIG_USB_GADGETFS=m
|
||||||
|
@ -115,14 +114,12 @@ CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
CONFIG_MMC_TEST=m
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_MMC_SPI=m
|
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
CONFIG_LEDS_GPIO=y
|
CONFIG_LEDS_GPIO=y
|
||||||
CONFIG_LEDS_TRIGGERS=y
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
CONFIG_DMADEVICES=y
|
CONFIG_DMADEVICES=y
|
||||||
|
@ -130,21 +127,23 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=m
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
CONFIG_UFS_FS=y
|
CONFIG_UBIFS_FS=y
|
||||||
CONFIG_NFS_FS=y
|
CONFIG_NFS_FS=y
|
||||||
CONFIG_NFS_V3=y
|
CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -155,5 +154,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
CONFIG_CRYPTO_PCBC=m
|
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -31,6 +28,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -74,8 +72,10 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_TUN=m
|
||||||
CONFIG_NET_ETHERNET=y
|
CONFIG_NET_ETHERNET=y
|
||||||
CONFIG_MACB=y
|
CONFIG_MACB=y
|
||||||
# CONFIG_NETDEV_1000 is not set
|
# CONFIG_NETDEV_1000 is not set
|
||||||
|
@ -104,6 +104,7 @@ CONFIG_I2C_GPIO=m
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_ATMEL=y
|
CONFIG_SPI_ATMEL=y
|
||||||
CONFIG_SPI_SPIDEV=m
|
CONFIG_SPI_SPIDEV=m
|
||||||
|
CONFIG_GPIO_SYSFS=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
|
@ -127,6 +128,7 @@ CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
CONFIG_USB_CDC_COMPOSITE=m
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
|
@ -141,11 +143,14 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=y
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
|
@ -155,7 +160,6 @@ CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -166,4 +170,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -30,6 +27,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -73,8 +71,10 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_TUN=m
|
||||||
CONFIG_NET_ETHERNET=y
|
CONFIG_NET_ETHERNET=y
|
||||||
CONFIG_MACB=y
|
CONFIG_MACB=y
|
||||||
# CONFIG_NETDEV_1000 is not set
|
# CONFIG_NETDEV_1000 is not set
|
||||||
|
@ -103,6 +103,7 @@ CONFIG_I2C_GPIO=m
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_ATMEL=y
|
CONFIG_SPI_ATMEL=y
|
||||||
CONFIG_SPI_SPIDEV=m
|
CONFIG_SPI_SPIDEV=m
|
||||||
|
CONFIG_GPIO_SYSFS=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
|
@ -126,6 +127,7 @@ CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
CONFIG_USB_CDC_COMPOSITE=m
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
|
@ -140,11 +142,14 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=y
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
|
@ -154,7 +159,6 @@ CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -165,4 +169,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -29,6 +26,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -74,6 +72,7 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
CONFIG_TUN=m
|
CONFIG_TUN=m
|
||||||
|
@ -107,6 +106,7 @@ CONFIG_GPIO_SYSFS=y
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
CONFIG_USB_GADGET=y
|
CONFIG_USB_GADGET=y
|
||||||
|
CONFIG_USB_GADGET_VBUS_DRAW=350
|
||||||
CONFIG_USB_ZERO=m
|
CONFIG_USB_ZERO=m
|
||||||
CONFIG_USB_ETH=m
|
CONFIG_USB_ETH=m
|
||||||
CONFIG_USB_GADGETFS=m
|
CONFIG_USB_GADGETFS=m
|
||||||
|
@ -116,14 +116,12 @@ CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
CONFIG_MMC_TEST=m
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_MMC_SPI=m
|
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
CONFIG_LEDS_GPIO=y
|
CONFIG_LEDS_GPIO=y
|
||||||
CONFIG_LEDS_TRIGGERS=y
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
CONFIG_LEDS_TRIGGER_TIMER=y
|
||||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
||||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
CONFIG_DMADEVICES=y
|
CONFIG_DMADEVICES=y
|
||||||
|
@ -131,21 +129,23 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=m
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
CONFIG_UFS_FS=y
|
CONFIG_UBIFS_FS=y
|
||||||
CONFIG_NFS_FS=y
|
CONFIG_NFS_FS=y
|
||||||
CONFIG_NFS_V3=y
|
CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -156,5 +156,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
CONFIG_CRYPTO_PCBC=m
|
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -32,6 +29,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -77,8 +75,10 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_TUN=m
|
||||||
CONFIG_NET_ETHERNET=y
|
CONFIG_NET_ETHERNET=y
|
||||||
CONFIG_MACB=y
|
CONFIG_MACB=y
|
||||||
# CONFIG_NETDEV_1000 is not set
|
# CONFIG_NETDEV_1000 is not set
|
||||||
|
@ -107,6 +107,7 @@ CONFIG_I2C_GPIO=m
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_ATMEL=y
|
CONFIG_SPI_ATMEL=y
|
||||||
CONFIG_SPI_SPIDEV=m
|
CONFIG_SPI_SPIDEV=m
|
||||||
|
CONFIG_GPIO_SYSFS=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
|
@ -130,6 +131,7 @@ CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
CONFIG_USB_CDC_COMPOSITE=m
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
|
@ -144,11 +146,14 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=y
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
|
@ -158,7 +163,6 @@ CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -169,4 +173,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
|
|
|
@ -2,20 +2,17 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
# CONFIG_IOSCHED_DEADLINE is not set
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
CONFIG_NO_HZ=y
|
CONFIG_NO_HZ=y
|
||||||
|
@ -31,6 +28,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -76,8 +74,10 @@ CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_NETDEVICES=y
|
CONFIG_NETDEVICES=y
|
||||||
|
CONFIG_TUN=m
|
||||||
CONFIG_NET_ETHERNET=y
|
CONFIG_NET_ETHERNET=y
|
||||||
CONFIG_MACB=y
|
CONFIG_MACB=y
|
||||||
# CONFIG_NETDEV_1000 is not set
|
# CONFIG_NETDEV_1000 is not set
|
||||||
|
@ -106,6 +106,7 @@ CONFIG_I2C_GPIO=m
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_ATMEL=y
|
CONFIG_SPI_ATMEL=y
|
||||||
CONFIG_SPI_SPIDEV=m
|
CONFIG_SPI_SPIDEV=m
|
||||||
|
CONFIG_GPIO_SYSFS=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
|
@ -129,6 +130,7 @@ CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
CONFIG_USB_CDC_COMPOSITE=m
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
|
@ -143,11 +145,14 @@ CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT3_FS=y
|
CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=y
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
|
@ -157,7 +162,6 @@ CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
CONFIG_NFSD=m
|
CONFIG_NFSD=m
|
||||||
CONFIG_NFSD_V3=y
|
CONFIG_NFSD_V3=y
|
||||||
CONFIG_SMB_FS=m
|
|
||||||
CONFIG_CIFS=m
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
CONFIG_NLS_CODEPAGE_850=m
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
@ -168,4 +172,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
|
|
|
@ -3,7 +3,6 @@ CONFIG_EXPERIMENTAL=y
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
|
||||||
CONFIG_RELAY=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
|
@ -11,7 +10,7 @@ CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
|
@ -26,6 +25,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -35,6 +35,7 @@ CONFIG_INET=y
|
||||||
CONFIG_IP_PNP=y
|
CONFIG_IP_PNP=y
|
||||||
CONFIG_IP_PNP_DHCP=y
|
CONFIG_IP_PNP_DHCP=y
|
||||||
CONFIG_NET_IPIP=m
|
CONFIG_NET_IPIP=m
|
||||||
|
CONFIG_NET_IPGRE_DEMUX=m
|
||||||
CONFIG_NET_IPGRE=m
|
CONFIG_NET_IPGRE=m
|
||||||
CONFIG_INET_AH=m
|
CONFIG_INET_AH=m
|
||||||
CONFIG_INET_ESP=m
|
CONFIG_INET_ESP=m
|
||||||
|
@ -58,16 +59,14 @@ CONFIG_MTD_BLOCK=y
|
||||||
CONFIG_MTD_CFI=y
|
CONFIG_MTD_CFI=y
|
||||||
CONFIG_MTD_CFI_AMDSTD=y
|
CONFIG_MTD_CFI_AMDSTD=y
|
||||||
CONFIG_MTD_PHYSMAP=y
|
CONFIG_MTD_PHYSMAP=y
|
||||||
CONFIG_MTD_DATAFLASH=m
|
|
||||||
CONFIG_MTD_M25P80=m
|
|
||||||
CONFIG_MTD_UBI=y
|
CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_PWM=m
|
CONFIG_ATMEL_PWM=m
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_ATMEL_SSC=m
|
CONFIG_ATMEL_SSC=m
|
||||||
CONFIG_EEPROM_AT24=m
|
|
||||||
# CONFIG_SCSI_PROC_FS is not set
|
# CONFIG_SCSI_PROC_FS is not set
|
||||||
CONFIG_BLK_DEV_SD=m
|
CONFIG_BLK_DEV_SD=m
|
||||||
CONFIG_BLK_DEV_SR=m
|
CONFIG_BLK_DEV_SR=m
|
||||||
|
@ -120,7 +119,6 @@ CONFIG_SND_MIXER_OSS=m
|
||||||
CONFIG_SND_PCM_OSS=m
|
CONFIG_SND_PCM_OSS=m
|
||||||
# CONFIG_SND_SUPPORT_OLD_API is not set
|
# CONFIG_SND_SUPPORT_OLD_API is not set
|
||||||
# CONFIG_SND_VERBOSE_PROCFS is not set
|
# CONFIG_SND_VERBOSE_PROCFS is not set
|
||||||
# CONFIG_SND_DRIVERS is not set
|
|
||||||
CONFIG_SND_AT73C213=m
|
CONFIG_SND_AT73C213=m
|
||||||
# CONFIG_HID_SUPPORT is not set
|
# CONFIG_HID_SUPPORT is not set
|
||||||
CONFIG_USB_GADGET=y
|
CONFIG_USB_GADGET=y
|
||||||
|
@ -131,16 +129,15 @@ CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
CONFIG_USB_CDC_COMPOSITE=m
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_MMC_SPI=m
|
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=m
|
CONFIG_LEDS_CLASS=y
|
||||||
CONFIG_LEDS_ATMEL_PWM=m
|
CONFIG_LEDS_ATMEL_PWM=m
|
||||||
CONFIG_LEDS_GPIO=m
|
CONFIG_LEDS_GPIO=m
|
||||||
CONFIG_LEDS_TRIGGERS=y
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
|
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
CONFIG_DMADEVICES=y
|
CONFIG_DMADEVICES=y
|
||||||
|
@ -149,20 +146,23 @@ CONFIG_EXT3_FS=y
|
||||||
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
CONFIG_EXT4_FS=y
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
CONFIG_PROC_KCORE=y
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
# CONFIG_JFFS2_FS_WRITEBUFFER is not set
|
|
||||||
CONFIG_UBIFS_FS=y
|
CONFIG_UBIFS_FS=y
|
||||||
CONFIG_MINIX_FS=m
|
|
||||||
CONFIG_NFS_FS=y
|
CONFIG_NFS_FS=y
|
||||||
CONFIG_NFS_V3=y
|
CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
CONFIG_NLS_ISO8859_1=m
|
CONFIG_NLS_ISO8859_1=m
|
||||||
CONFIG_NLS_UTF8=m
|
CONFIG_NLS_UTF8=m
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
|
@ -170,6 +170,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
# CONFIG_CRYPTO_HW is not set
|
|
||||||
CONFIG_CRC_T10DIF=m
|
|
||||||
|
|
|
@ -2,22 +2,15 @@ CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_BSD_PROCESS_ACCT=y
|
|
||||||
CONFIG_BSD_PROCESS_ACCT_V3=y
|
|
||||||
CONFIG_TASKSTATS=y
|
|
||||||
CONFIG_TASK_DELAY_ACCT=y
|
|
||||||
CONFIG_AUDIT=y
|
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
|
||||||
CONFIG_RELAY=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_SLUB_DEBUG is not set
|
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
|
@ -33,6 +26,7 @@ CONFIG_CPU_FREQ=y
|
||||||
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
CONFIG_CPU_FREQ_AT32AP=y
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -54,18 +48,18 @@ CONFIG_MTD_BLOCK=y
|
||||||
CONFIG_MTD_CFI=y
|
CONFIG_MTD_CFI=y
|
||||||
CONFIG_MTD_CFI_AMDSTD=y
|
CONFIG_MTD_CFI_AMDSTD=y
|
||||||
CONFIG_MTD_PHYSMAP=y
|
CONFIG_MTD_PHYSMAP=y
|
||||||
CONFIG_MTD_DATAFLASH=m
|
CONFIG_MTD_UBI=y
|
||||||
CONFIG_MTD_M25P80=m
|
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_PWM=m
|
CONFIG_ATMEL_PWM=m
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_ATMEL_SSC=m
|
CONFIG_ATMEL_SSC=m
|
||||||
CONFIG_EEPROM_AT24=m
|
|
||||||
# CONFIG_SCSI_PROC_FS is not set
|
# CONFIG_SCSI_PROC_FS is not set
|
||||||
CONFIG_BLK_DEV_SD=m
|
CONFIG_BLK_DEV_SD=m
|
||||||
CONFIG_BLK_DEV_SR=m
|
CONFIG_BLK_DEV_SR=m
|
||||||
|
# CONFIG_SCSI_LOWLEVEL is not set
|
||||||
CONFIG_ATA=m
|
CONFIG_ATA=m
|
||||||
# CONFIG_SATA_PMP is not set
|
# CONFIG_SATA_PMP is not set
|
||||||
CONFIG_PATA_AT32=m
|
CONFIG_PATA_AT32=m
|
||||||
|
@ -77,6 +71,7 @@ CONFIG_PPP_ASYNC=m
|
||||||
CONFIG_PPP_DEFLATE=m
|
CONFIG_PPP_DEFLATE=m
|
||||||
CONFIG_PPP_BSDCOMP=m
|
CONFIG_PPP_BSDCOMP=m
|
||||||
CONFIG_INPUT=m
|
CONFIG_INPUT=m
|
||||||
|
CONFIG_INPUT_EVDEV=m
|
||||||
# CONFIG_KEYBOARD_ATKBD is not set
|
# CONFIG_KEYBOARD_ATKBD is not set
|
||||||
CONFIG_KEYBOARD_GPIO=m
|
CONFIG_KEYBOARD_GPIO=m
|
||||||
# CONFIG_MOUSE_PS2 is not set
|
# CONFIG_MOUSE_PS2 is not set
|
||||||
|
@ -106,7 +101,6 @@ CONFIG_SND_PCM_OSS=m
|
||||||
CONFIG_SND_AT73C213=m
|
CONFIG_SND_AT73C213=m
|
||||||
# CONFIG_HID_SUPPORT is not set
|
# CONFIG_HID_SUPPORT is not set
|
||||||
CONFIG_USB_GADGET=y
|
CONFIG_USB_GADGET=y
|
||||||
CONFIG_USB_GADGET_DEBUG_FS=y
|
|
||||||
CONFIG_USB_ZERO=m
|
CONFIG_USB_ZERO=m
|
||||||
CONFIG_USB_ETH=m
|
CONFIG_USB_ETH=m
|
||||||
CONFIG_USB_GADGETFS=m
|
CONFIG_USB_GADGETFS=m
|
||||||
|
@ -116,36 +110,39 @@ CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
CONFIG_MMC_TEST=m
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_MMC_SPI=m
|
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=y
|
CONFIG_LEDS_CLASS=y
|
||||||
CONFIG_LEDS_ATMEL_PWM=m
|
CONFIG_LEDS_ATMEL_PWM=m
|
||||||
CONFIG_LEDS_GPIO=y
|
CONFIG_LEDS_GPIO=m
|
||||||
CONFIG_LEDS_TRIGGERS=y
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
CONFIG_LEDS_TRIGGER_TIMER=y
|
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
|
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
CONFIG_DMADEVICES=y
|
CONFIG_DMADEVICES=y
|
||||||
CONFIG_DW_DMAC=y
|
CONFIG_EXT2_FS=y
|
||||||
CONFIG_EXT2_FS=m
|
CONFIG_EXT3_FS=y
|
||||||
CONFIG_EXT3_FS=m
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
# CONFIG_EXT3_FS_XATTR is not set
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
CONFIG_PROC_KCORE=y
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
CONFIG_CONFIGFS_FS=m
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
|
CONFIG_UBIFS_FS=y
|
||||||
# CONFIG_NETWORK_FILESYSTEMS is not set
|
# CONFIG_NETWORK_FILESYSTEMS is not set
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
CONFIG_NLS_ISO8859_1=m
|
CONFIG_NLS_ISO8859_1=m
|
||||||
CONFIG_NLS_UTF8=m
|
CONFIG_NLS_UTF8=m
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
CONFIG_DEBUG_FS=y
|
CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
CONFIG_CRC_T10DIF=m
|
|
||||||
|
|
|
@ -1,19 +1,32 @@
|
||||||
CONFIG_EXPERIMENTAL=y
|
CONFIG_EXPERIMENTAL=y
|
||||||
# CONFIG_LOCALVERSION_AUTO is not set
|
# CONFIG_LOCALVERSION_AUTO is not set
|
||||||
|
CONFIG_SYSVIPC=y
|
||||||
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
CONFIG_RELAY=y
|
||||||
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
# CONFIG_BASE_FULL is not set
|
# CONFIG_BASE_FULL is not set
|
||||||
# CONFIG_FUTEX is not set
|
|
||||||
# CONFIG_EPOLL is not set
|
|
||||||
# CONFIG_SIGNALFD is not set
|
|
||||||
# CONFIG_TIMERFD is not set
|
|
||||||
# CONFIG_EVENTFD is not set
|
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_SLOB=y
|
CONFIG_PROFILING=y
|
||||||
# CONFIG_BLOCK is not set
|
CONFIG_OPROFILE=m
|
||||||
|
# CONFIG_KPROBES is not set
|
||||||
|
CONFIG_MODULES=y
|
||||||
|
CONFIG_MODULE_UNLOAD=y
|
||||||
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
|
# CONFIG_IOSCHED_DEADLINE is not set
|
||||||
|
CONFIG_NO_HZ=y
|
||||||
|
CONFIG_HIGH_RES_TIMERS=y
|
||||||
CONFIG_BOARD_ATSTK1004=y
|
CONFIG_BOARD_ATSTK1004=y
|
||||||
# CONFIG_OWNERSHIP_TRACE is not set
|
# CONFIG_OWNERSHIP_TRACE is not set
|
||||||
|
CONFIG_NMI_DEBUGGING=y
|
||||||
|
CONFIG_PM=y
|
||||||
|
CONFIG_CPU_FREQ=y
|
||||||
|
# CONFIG_CPU_FREQ_STAT is not set
|
||||||
|
CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
|
||||||
|
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||||
|
CONFIG_CPU_FREQ_AT32AP=y
|
||||||
|
CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
|
||||||
CONFIG_NET=y
|
CONFIG_NET=y
|
||||||
CONFIG_PACKET=y
|
CONFIG_PACKET=y
|
||||||
CONFIG_UNIX=y
|
CONFIG_UNIX=y
|
||||||
|
@ -31,40 +44,104 @@ CONFIG_MTD=y
|
||||||
CONFIG_MTD_PARTITIONS=y
|
CONFIG_MTD_PARTITIONS=y
|
||||||
CONFIG_MTD_CMDLINE_PARTS=y
|
CONFIG_MTD_CMDLINE_PARTS=y
|
||||||
CONFIG_MTD_CHAR=y
|
CONFIG_MTD_CHAR=y
|
||||||
|
CONFIG_MTD_BLOCK=y
|
||||||
CONFIG_MTD_CFI=y
|
CONFIG_MTD_CFI=y
|
||||||
CONFIG_MTD_CFI_AMDSTD=y
|
CONFIG_MTD_CFI_AMDSTD=y
|
||||||
CONFIG_MTD_PHYSMAP=y
|
CONFIG_MTD_PHYSMAP=y
|
||||||
# CONFIG_MISC_DEVICES is not set
|
CONFIG_MTD_UBI=y
|
||||||
# CONFIG_INPUT is not set
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
|
CONFIG_BLK_DEV_NBD=m
|
||||||
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
|
CONFIG_ATMEL_PWM=m
|
||||||
|
CONFIG_ATMEL_TCLIB=y
|
||||||
|
CONFIG_ATMEL_SSC=m
|
||||||
|
# CONFIG_SCSI_PROC_FS is not set
|
||||||
|
CONFIG_BLK_DEV_SD=m
|
||||||
|
CONFIG_BLK_DEV_SR=m
|
||||||
|
# CONFIG_SCSI_LOWLEVEL is not set
|
||||||
|
CONFIG_ATA=m
|
||||||
|
# CONFIG_SATA_PMP is not set
|
||||||
|
CONFIG_PATA_AT32=m
|
||||||
|
CONFIG_NETDEVICES=y
|
||||||
|
# CONFIG_NETDEV_1000 is not set
|
||||||
|
# CONFIG_NETDEV_10000 is not set
|
||||||
|
CONFIG_PPP=m
|
||||||
|
CONFIG_PPP_ASYNC=m
|
||||||
|
CONFIG_PPP_DEFLATE=m
|
||||||
|
CONFIG_PPP_BSDCOMP=m
|
||||||
|
CONFIG_INPUT=m
|
||||||
|
CONFIG_INPUT_EVDEV=m
|
||||||
|
# CONFIG_KEYBOARD_ATKBD is not set
|
||||||
|
CONFIG_KEYBOARD_GPIO=m
|
||||||
|
# CONFIG_MOUSE_PS2 is not set
|
||||||
|
CONFIG_MOUSE_GPIO=m
|
||||||
# CONFIG_SERIO is not set
|
# CONFIG_SERIO is not set
|
||||||
# CONFIG_VT is not set
|
# CONFIG_VT is not set
|
||||||
# CONFIG_DEVKMEM is not set
|
# CONFIG_DEVKMEM is not set
|
||||||
CONFIG_SERIAL_ATMEL=y
|
CONFIG_SERIAL_ATMEL=y
|
||||||
CONFIG_SERIAL_ATMEL_CONSOLE=y
|
CONFIG_SERIAL_ATMEL_CONSOLE=y
|
||||||
# CONFIG_SERIAL_ATMEL_PDC is not set
|
|
||||||
# CONFIG_LEGACY_PTYS is not set
|
# CONFIG_LEGACY_PTYS is not set
|
||||||
# CONFIG_HW_RANDOM is not set
|
# CONFIG_HW_RANDOM is not set
|
||||||
|
CONFIG_I2C=m
|
||||||
|
CONFIG_I2C_CHARDEV=m
|
||||||
|
CONFIG_I2C_GPIO=m
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_ATMEL=y
|
CONFIG_SPI_ATMEL=y
|
||||||
|
CONFIG_SPI_SPIDEV=m
|
||||||
|
CONFIG_GPIO_SYSFS=y
|
||||||
# CONFIG_HWMON is not set
|
# CONFIG_HWMON is not set
|
||||||
CONFIG_WATCHDOG=y
|
CONFIG_WATCHDOG=y
|
||||||
CONFIG_AT32AP700X_WDT=y
|
CONFIG_AT32AP700X_WDT=y
|
||||||
CONFIG_FB=y
|
CONFIG_FB=y
|
||||||
CONFIG_FB_ATMEL=y
|
CONFIG_FB_ATMEL=y
|
||||||
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
CONFIG_BACKLIGHT_LCD_SUPPORT=y
|
||||||
|
CONFIG_LCD_CLASS_DEVICE=y
|
||||||
CONFIG_LCD_LTV350QV=y
|
CONFIG_LCD_LTV350QV=y
|
||||||
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
|
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
|
||||||
CONFIG_USB_GADGET=y
|
CONFIG_USB_GADGET=y
|
||||||
CONFIG_USB_ETH=y
|
CONFIG_USB_ZERO=m
|
||||||
# CONFIG_USB_ETH_RNDIS is not set
|
CONFIG_USB_ETH=m
|
||||||
|
CONFIG_USB_GADGETFS=m
|
||||||
|
CONFIG_USB_FILE_STORAGE=m
|
||||||
|
CONFIG_USB_G_SERIAL=m
|
||||||
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
|
CONFIG_MMC_ATMELMCI=y
|
||||||
|
CONFIG_NEW_LEDS=y
|
||||||
|
CONFIG_LEDS_CLASS=y
|
||||||
|
CONFIG_LEDS_ATMEL_PWM=m
|
||||||
|
CONFIG_LEDS_GPIO=m
|
||||||
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
|
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||||
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
# CONFIG_RTC_INTF_PROC is not set
|
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
|
CONFIG_DMADEVICES=y
|
||||||
|
CONFIG_EXT2_FS=y
|
||||||
|
CONFIG_EXT3_FS=y
|
||||||
|
# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
|
||||||
|
# CONFIG_EXT3_FS_XATTR is not set
|
||||||
|
CONFIG_EXT4_FS=y
|
||||||
|
# CONFIG_EXT4_FS_XATTR is not set
|
||||||
# CONFIG_DNOTIFY is not set
|
# CONFIG_DNOTIFY is not set
|
||||||
|
CONFIG_FUSE_FS=m
|
||||||
|
CONFIG_MSDOS_FS=m
|
||||||
|
CONFIG_VFAT_FS=m
|
||||||
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
CONFIG_PROC_KCORE=y
|
CONFIG_PROC_KCORE=y
|
||||||
# CONFIG_PROC_PAGE_MONITOR is not set
|
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
# CONFIG_JFFS2_FS_WRITEBUFFER is not set
|
CONFIG_UBIFS_FS=y
|
||||||
# CONFIG_NETWORK_FILESYSTEMS is not set
|
# CONFIG_NETWORK_FILESYSTEMS is not set
|
||||||
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
|
CONFIG_NLS_ISO8859_1=m
|
||||||
|
CONFIG_NLS_UTF8=m
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
|
CONFIG_DEBUG_FS=y
|
||||||
|
CONFIG_DEBUG_KERNEL=y
|
||||||
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
|
CONFIG_FRAME_POINTER=y
|
||||||
|
|
|
@ -3,7 +3,6 @@ CONFIG_EXPERIMENTAL=y
|
||||||
CONFIG_SYSVIPC=y
|
CONFIG_SYSVIPC=y
|
||||||
CONFIG_POSIX_MQUEUE=y
|
CONFIG_POSIX_MQUEUE=y
|
||||||
CONFIG_LOG_BUF_SHIFT=14
|
CONFIG_LOG_BUF_SHIFT=14
|
||||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
|
||||||
CONFIG_RELAY=y
|
CONFIG_RELAY=y
|
||||||
CONFIG_BLK_DEV_INITRD=y
|
CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_SYSCTL_SYSCALL is not set
|
# CONFIG_SYSCTL_SYSCALL is not set
|
||||||
|
@ -11,7 +10,7 @@ CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
|
@ -37,6 +36,7 @@ CONFIG_INET=y
|
||||||
CONFIG_IP_PNP=y
|
CONFIG_IP_PNP=y
|
||||||
CONFIG_IP_PNP_DHCP=y
|
CONFIG_IP_PNP_DHCP=y
|
||||||
CONFIG_NET_IPIP=m
|
CONFIG_NET_IPIP=m
|
||||||
|
CONFIG_NET_IPGRE_DEMUX=m
|
||||||
CONFIG_NET_IPGRE=m
|
CONFIG_NET_IPGRE=m
|
||||||
CONFIG_INET_AH=m
|
CONFIG_INET_AH=m
|
||||||
CONFIG_INET_ESP=m
|
CONFIG_INET_ESP=m
|
||||||
|
@ -60,15 +60,13 @@ CONFIG_MTD_BLOCK=y
|
||||||
CONFIG_MTD_CFI=y
|
CONFIG_MTD_CFI=y
|
||||||
CONFIG_MTD_CFI_AMDSTD=y
|
CONFIG_MTD_CFI_AMDSTD=y
|
||||||
CONFIG_MTD_PHYSMAP=y
|
CONFIG_MTD_PHYSMAP=y
|
||||||
CONFIG_MTD_DATAFLASH=m
|
|
||||||
CONFIG_MTD_DATAFLASH_OTP=y
|
|
||||||
CONFIG_MTD_M25P80=m
|
|
||||||
CONFIG_MTD_NAND=y
|
CONFIG_MTD_NAND=y
|
||||||
CONFIG_MTD_NAND_ATMEL=y
|
CONFIG_MTD_NAND_ATMEL=y
|
||||||
CONFIG_MTD_UBI=y
|
CONFIG_MTD_UBI=y
|
||||||
CONFIG_BLK_DEV_LOOP=m
|
CONFIG_BLK_DEV_LOOP=m
|
||||||
CONFIG_BLK_DEV_NBD=m
|
CONFIG_BLK_DEV_NBD=m
|
||||||
CONFIG_BLK_DEV_RAM=m
|
CONFIG_BLK_DEV_RAM=m
|
||||||
|
CONFIG_MISC_DEVICES=y
|
||||||
CONFIG_ATMEL_PWM=m
|
CONFIG_ATMEL_PWM=m
|
||||||
CONFIG_ATMEL_TCLIB=y
|
CONFIG_ATMEL_TCLIB=y
|
||||||
CONFIG_ATMEL_SSC=m
|
CONFIG_ATMEL_SSC=m
|
||||||
|
@ -132,17 +130,17 @@ CONFIG_USB_ETH=m
|
||||||
CONFIG_USB_GADGETFS=m
|
CONFIG_USB_GADGETFS=m
|
||||||
CONFIG_USB_FILE_STORAGE=m
|
CONFIG_USB_FILE_STORAGE=m
|
||||||
CONFIG_USB_G_SERIAL=m
|
CONFIG_USB_G_SERIAL=m
|
||||||
|
CONFIG_USB_CDC_COMPOSITE=m
|
||||||
CONFIG_MMC=y
|
CONFIG_MMC=y
|
||||||
|
CONFIG_MMC_TEST=m
|
||||||
CONFIG_MMC_ATMELMCI=y
|
CONFIG_MMC_ATMELMCI=y
|
||||||
CONFIG_MMC_SPI=m
|
|
||||||
CONFIG_NEW_LEDS=y
|
CONFIG_NEW_LEDS=y
|
||||||
CONFIG_LEDS_CLASS=m
|
CONFIG_LEDS_CLASS=y
|
||||||
CONFIG_LEDS_ATMEL_PWM=m
|
CONFIG_LEDS_ATMEL_PWM=m
|
||||||
CONFIG_LEDS_GPIO=m
|
CONFIG_LEDS_GPIO=m
|
||||||
CONFIG_LEDS_TRIGGERS=y
|
CONFIG_LEDS_TRIGGERS=y
|
||||||
CONFIG_LEDS_TRIGGER_TIMER=m
|
CONFIG_LEDS_TRIGGER_TIMER=m
|
||||||
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
CONFIG_LEDS_TRIGGER_HEARTBEAT=m
|
||||||
CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
|
|
||||||
CONFIG_RTC_CLASS=y
|
CONFIG_RTC_CLASS=y
|
||||||
CONFIG_RTC_DRV_AT32AP700X=y
|
CONFIG_RTC_DRV_AT32AP700X=y
|
||||||
CONFIG_DMADEVICES=y
|
CONFIG_DMADEVICES=y
|
||||||
|
@ -156,15 +154,18 @@ CONFIG_EXT4_FS=y
|
||||||
CONFIG_FUSE_FS=m
|
CONFIG_FUSE_FS=m
|
||||||
CONFIG_MSDOS_FS=m
|
CONFIG_MSDOS_FS=m
|
||||||
CONFIG_VFAT_FS=m
|
CONFIG_VFAT_FS=m
|
||||||
|
CONFIG_FAT_DEFAULT_CODEPAGE=850
|
||||||
CONFIG_PROC_KCORE=y
|
CONFIG_PROC_KCORE=y
|
||||||
CONFIG_TMPFS=y
|
CONFIG_TMPFS=y
|
||||||
|
CONFIG_CONFIGFS_FS=y
|
||||||
CONFIG_JFFS2_FS=y
|
CONFIG_JFFS2_FS=y
|
||||||
CONFIG_UBIFS_FS=y
|
CONFIG_UBIFS_FS=y
|
||||||
CONFIG_MINIX_FS=m
|
|
||||||
CONFIG_NFS_FS=y
|
CONFIG_NFS_FS=y
|
||||||
CONFIG_NFS_V3=y
|
CONFIG_NFS_V3=y
|
||||||
CONFIG_ROOT_NFS=y
|
CONFIG_ROOT_NFS=y
|
||||||
|
CONFIG_CIFS=m
|
||||||
CONFIG_NLS_CODEPAGE_437=m
|
CONFIG_NLS_CODEPAGE_437=m
|
||||||
|
CONFIG_NLS_CODEPAGE_850=m
|
||||||
CONFIG_NLS_ISO8859_1=m
|
CONFIG_NLS_ISO8859_1=m
|
||||||
CONFIG_NLS_UTF8=m
|
CONFIG_NLS_UTF8=m
|
||||||
CONFIG_MAGIC_SYSRQ=y
|
CONFIG_MAGIC_SYSRQ=y
|
||||||
|
@ -172,7 +173,3 @@ CONFIG_DEBUG_FS=y
|
||||||
CONFIG_DEBUG_KERNEL=y
|
CONFIG_DEBUG_KERNEL=y
|
||||||
CONFIG_DETECT_HUNG_TASK=y
|
CONFIG_DETECT_HUNG_TASK=y
|
||||||
CONFIG_FRAME_POINTER=y
|
CONFIG_FRAME_POINTER=y
|
||||||
# CONFIG_RCU_CPU_STALL_DETECTOR is not set
|
|
||||||
CONFIG_CRYPTO_FIPS=y
|
|
||||||
# CONFIG_CRYPTO_HW is not set
|
|
||||||
CONFIG_CRC_T10DIF=m
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
# CONFIG_BLK_DEV_BSG is not set
|
# CONFIG_BLK_DEV_BSG is not set
|
||||||
|
|
|
@ -12,7 +12,7 @@ CONFIG_BLK_DEV_INITRD=y
|
||||||
# CONFIG_COMPAT_BRK is not set
|
# CONFIG_COMPAT_BRK is not set
|
||||||
CONFIG_PROFILING=y
|
CONFIG_PROFILING=y
|
||||||
CONFIG_OPROFILE=m
|
CONFIG_OPROFILE=m
|
||||||
CONFIG_KPROBES=y
|
# CONFIG_KPROBES is not set
|
||||||
CONFIG_MODULES=y
|
CONFIG_MODULES=y
|
||||||
CONFIG_MODULE_UNLOAD=y
|
CONFIG_MODULE_UNLOAD=y
|
||||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
CONFIG_MODULE_FORCE_UNLOAD=y
|
||||||
|
|
|
@ -15,20 +15,6 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/signal.h>
|
#include <linux/signal.h>
|
||||||
|
|
||||||
/* kernel/process.c */
|
|
||||||
asmlinkage int sys_fork(struct pt_regs *);
|
|
||||||
asmlinkage int sys_clone(unsigned long, unsigned long,
|
|
||||||
unsigned long, unsigned long,
|
|
||||||
struct pt_regs *);
|
|
||||||
asmlinkage int sys_vfork(struct pt_regs *);
|
|
||||||
asmlinkage int sys_execve(const char __user *, char __user *__user *,
|
|
||||||
char __user *__user *, struct pt_regs *);
|
|
||||||
|
|
||||||
/* kernel/signal.c */
|
|
||||||
asmlinkage int sys_sigaltstack(const stack_t __user *, stack_t __user *,
|
|
||||||
struct pt_regs *);
|
|
||||||
asmlinkage int sys_rt_sigreturn(struct pt_regs *);
|
|
||||||
|
|
||||||
/* mm/cache.c */
|
/* mm/cache.c */
|
||||||
asmlinkage int sys_cacheflush(int, void __user *, size_t);
|
asmlinkage int sys_cacheflush(int, void __user *, size_t);
|
||||||
|
|
||||||
|
|
|
@ -367,14 +367,13 @@ asmlinkage int sys_fork(struct pt_regs *regs)
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
asmlinkage int sys_clone(unsigned long clone_flags, unsigned long newsp,
|
||||||
unsigned long parent_tidptr,
|
void __user *parent_tidptr, void __user *child_tidptr,
|
||||||
unsigned long child_tidptr, struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
if (!newsp)
|
if (!newsp)
|
||||||
newsp = regs->sp;
|
newsp = regs->sp;
|
||||||
return do_fork(clone_flags, newsp, regs, 0,
|
return do_fork(clone_flags, newsp, regs, 0, parent_tidptr,
|
||||||
(int __user *)parent_tidptr,
|
child_tidptr);
|
||||||
(int __user *)child_tidptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
asmlinkage int sys_vfork(struct pt_regs *regs)
|
asmlinkage int sys_vfork(struct pt_regs *regs)
|
||||||
|
|
|
@ -35,7 +35,6 @@ static struct clocksource counter = {
|
||||||
.rating = 50,
|
.rating = 50,
|
||||||
.read = read_cycle_count,
|
.read = read_cycle_count,
|
||||||
.mask = CLOCKSOURCE_MASK(32),
|
.mask = CLOCKSOURCE_MASK(32),
|
||||||
.shift = 16,
|
|
||||||
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -123,9 +122,7 @@ void __init time_init(void)
|
||||||
|
|
||||||
/* figure rate for counter */
|
/* figure rate for counter */
|
||||||
counter_hz = clk_get_rate(boot_cpu_data.clk);
|
counter_hz = clk_get_rate(boot_cpu_data.clk);
|
||||||
counter.mult = clocksource_hz2mult(counter_hz, counter.shift);
|
ret = clocksource_register_hz(&counter, counter_hz);
|
||||||
|
|
||||||
ret = clocksource_register(&counter);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
pr_debug("timer: could not register clocksource: %d\n", ret);
|
pr_debug("timer: could not register clocksource: %d\n", ret);
|
||||||
|
|
||||||
|
|
|
@ -618,7 +618,7 @@ pfm_get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* forward declaration */
|
/* forward declaration */
|
||||||
static static const struct dentry_operations pfmfs_dentry_operations;
|
static const struct dentry_operations pfmfs_dentry_operations;
|
||||||
|
|
||||||
static struct dentry *
|
static struct dentry *
|
||||||
pfmfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
|
pfmfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data)
|
||||||
|
|
|
@ -38,7 +38,7 @@ huge_pte_alloc(struct mm_struct *mm, unsigned long addr, unsigned long sz)
|
||||||
if (pud) {
|
if (pud) {
|
||||||
pmd = pmd_alloc(mm, pud, taddr);
|
pmd = pmd_alloc(mm, pud, taddr);
|
||||||
if (pmd)
|
if (pmd)
|
||||||
pte = pte_alloc_map(mm, pmd, taddr);
|
pte = pte_alloc_map(mm, NULL, pmd, taddr);
|
||||||
}
|
}
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,6 +77,9 @@
|
||||||
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
||||||
#define MADV_HWPOISON 100 /* poison a page for testing */
|
#define MADV_HWPOISON 100 /* poison a page for testing */
|
||||||
|
|
||||||
|
#define MADV_HUGEPAGE 14 /* Worth backing with hugepages */
|
||||||
|
#define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */
|
||||||
|
|
||||||
/* compatibility flags */
|
/* compatibility flags */
|
||||||
#define MAP_FILE 0
|
#define MAP_FILE 0
|
||||||
|
|
||||||
|
|
|
@ -46,17 +46,9 @@ static DEFINE_SPINLOCK(dbe_lock);
|
||||||
void *module_alloc(unsigned long size)
|
void *module_alloc(unsigned long size)
|
||||||
{
|
{
|
||||||
#ifdef MODULE_START
|
#ifdef MODULE_START
|
||||||
struct vm_struct *area;
|
return __vmalloc_node_range(size, 1, MODULE_START, MODULE_END,
|
||||||
|
GFP_KERNEL, PAGE_KERNEL, -1,
|
||||||
size = PAGE_ALIGN(size);
|
__builtin_return_address(0));
|
||||||
if (!size)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
area = __get_vm_area(size, VM_ALLOC, MODULE_START, MODULE_END);
|
|
||||||
if (!area)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
|
|
||||||
#else
|
#else
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
|
@ -59,6 +59,9 @@
|
||||||
#define MADV_MERGEABLE 65 /* KSM may merge identical pages */
|
#define MADV_MERGEABLE 65 /* KSM may merge identical pages */
|
||||||
#define MADV_UNMERGEABLE 66 /* KSM may not merge identical pages */
|
#define MADV_UNMERGEABLE 66 /* KSM may not merge identical pages */
|
||||||
|
|
||||||
|
#define MADV_HUGEPAGE 67 /* Worth backing with hugepages */
|
||||||
|
#define MADV_NOHUGEPAGE 68 /* Not worth backing with hugepages */
|
||||||
|
|
||||||
/* compatibility flags */
|
/* compatibility flags */
|
||||||
#define MAP_FILE 0
|
#define MAP_FILE 0
|
||||||
#define MAP_VARIABLE 0
|
#define MAP_VARIABLE 0
|
||||||
|
|
|
@ -16,6 +16,16 @@
|
||||||
|
|
||||||
#ifdef __HAVE_ARCH_PTE_SPECIAL
|
#ifdef __HAVE_ARCH_PTE_SPECIAL
|
||||||
|
|
||||||
|
static inline void get_huge_page_tail(struct page *page)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* __split_huge_page_refcount() cannot run
|
||||||
|
* from under us.
|
||||||
|
*/
|
||||||
|
VM_BUG_ON(atomic_read(&page->_count) < 0);
|
||||||
|
atomic_inc(&page->_count);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The performance critical leaf functions are made noinline otherwise gcc
|
* The performance critical leaf functions are made noinline otherwise gcc
|
||||||
* inlines everything into a single function which results in too much
|
* inlines everything into a single function which results in too much
|
||||||
|
@ -47,6 +57,8 @@ static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
|
||||||
put_page(page);
|
put_page(page);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (PageTail(page))
|
||||||
|
get_huge_page_tail(page);
|
||||||
pages[*nr] = page;
|
pages[*nr] = page;
|
||||||
(*nr)++;
|
(*nr)++;
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm,
|
||||||
if (pud) {
|
if (pud) {
|
||||||
pmd = pmd_alloc(mm, pud, addr);
|
pmd = pmd_alloc(mm, pud, addr);
|
||||||
if (pmd)
|
if (pmd)
|
||||||
pte = pte_alloc_map(mm, pmd, addr);
|
pte = pte_alloc_map(mm, NULL, pmd, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,17 +23,11 @@
|
||||||
|
|
||||||
static void *module_map(unsigned long size)
|
static void *module_map(unsigned long size)
|
||||||
{
|
{
|
||||||
struct vm_struct *area;
|
if (PAGE_ALIGN(size) > MODULES_LEN)
|
||||||
|
|
||||||
size = PAGE_ALIGN(size);
|
|
||||||
if (!size || size > MODULES_LEN)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
|
||||||
area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
|
GFP_KERNEL, PAGE_KERNEL, -1,
|
||||||
if (!area)
|
__builtin_return_address(0));
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return __vmalloc_area(area, GFP_KERNEL, PAGE_KERNEL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *dot2underscore(char *name)
|
static char *dot2underscore(char *name)
|
||||||
|
|
|
@ -50,7 +50,7 @@ static inline int io_remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned
|
||||||
end = PGDIR_SIZE;
|
end = PGDIR_SIZE;
|
||||||
offset -= address;
|
offset -= address;
|
||||||
do {
|
do {
|
||||||
pte_t * pte = pte_alloc_map(mm, pmd, address);
|
pte_t *pte = pte_alloc_map(mm, NULL, pmd, address);
|
||||||
if (!pte)
|
if (!pte)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
|
io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
|
||||||
|
|
|
@ -92,7 +92,7 @@ static inline int io_remap_pmd_range(struct mm_struct *mm, pmd_t * pmd, unsigned
|
||||||
end = PGDIR_SIZE;
|
end = PGDIR_SIZE;
|
||||||
offset -= address;
|
offset -= address;
|
||||||
do {
|
do {
|
||||||
pte_t * pte = pte_alloc_map(mm, pmd, address);
|
pte_t *pte = pte_alloc_map(mm, NULL, pmd, address);
|
||||||
if (!pte)
|
if (!pte)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
|
io_remap_pte_range(mm, pte, address, end - address, address + offset, prot, space);
|
||||||
|
|
|
@ -214,7 +214,7 @@ pte_t *huge_pte_alloc(struct mm_struct *mm,
|
||||||
if (pud) {
|
if (pud) {
|
||||||
pmd = pmd_alloc(mm, pud, addr);
|
pmd = pmd_alloc(mm, pud, addr);
|
||||||
if (pmd)
|
if (pmd)
|
||||||
pte = pte_alloc_map(mm, pmd, addr);
|
pte = pte_alloc_map(mm, NULL, pmd, addr);
|
||||||
}
|
}
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ static int init_stub_pte(struct mm_struct *mm, unsigned long proc,
|
||||||
if (!pmd)
|
if (!pmd)
|
||||||
goto out_pmd;
|
goto out_pmd;
|
||||||
|
|
||||||
pte = pte_alloc_map(mm, pmd, proc);
|
pte = pte_alloc_map(mm, NULL, pmd, proc);
|
||||||
if (!pte)
|
if (!pte)
|
||||||
goto out_pte;
|
goto out_pte;
|
||||||
|
|
||||||
|
|
|
@ -822,6 +822,7 @@ extern bool kvm_rebooting;
|
||||||
#define KVM_ARCH_WANT_MMU_NOTIFIER
|
#define KVM_ARCH_WANT_MMU_NOTIFIER
|
||||||
int kvm_unmap_hva(struct kvm *kvm, unsigned long hva);
|
int kvm_unmap_hva(struct kvm *kvm, unsigned long hva);
|
||||||
int kvm_age_hva(struct kvm *kvm, unsigned long hva);
|
int kvm_age_hva(struct kvm *kvm, unsigned long hva);
|
||||||
|
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva);
|
||||||
void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
|
void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
|
||||||
int cpuid_maxphyaddr(struct kvm_vcpu *vcpu);
|
int cpuid_maxphyaddr(struct kvm_vcpu *vcpu);
|
||||||
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
|
int kvm_cpu_has_interrupt(struct kvm_vcpu *vcpu);
|
||||||
|
|
|
@ -435,6 +435,11 @@ static inline void pte_update(struct mm_struct *mm, unsigned long addr,
|
||||||
{
|
{
|
||||||
PVOP_VCALL3(pv_mmu_ops.pte_update, mm, addr, ptep);
|
PVOP_VCALL3(pv_mmu_ops.pte_update, mm, addr, ptep);
|
||||||
}
|
}
|
||||||
|
static inline void pmd_update(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
PVOP_VCALL3(pv_mmu_ops.pmd_update, mm, addr, pmdp);
|
||||||
|
}
|
||||||
|
|
||||||
static inline void pte_update_defer(struct mm_struct *mm, unsigned long addr,
|
static inline void pte_update_defer(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep)
|
pte_t *ptep)
|
||||||
|
@ -442,6 +447,12 @@ static inline void pte_update_defer(struct mm_struct *mm, unsigned long addr,
|
||||||
PVOP_VCALL3(pv_mmu_ops.pte_update_defer, mm, addr, ptep);
|
PVOP_VCALL3(pv_mmu_ops.pte_update_defer, mm, addr, ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void pmd_update_defer(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
PVOP_VCALL3(pv_mmu_ops.pmd_update_defer, mm, addr, pmdp);
|
||||||
|
}
|
||||||
|
|
||||||
static inline pte_t __pte(pteval_t val)
|
static inline pte_t __pte(pteval_t val)
|
||||||
{
|
{
|
||||||
pteval_t ret;
|
pteval_t ret;
|
||||||
|
@ -543,6 +554,20 @@ static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
|
||||||
PVOP_VCALL4(pv_mmu_ops.set_pte_at, mm, addr, ptep, pte.pte);
|
PVOP_VCALL4(pv_mmu_ops.set_pte_at, mm, addr, ptep, pte.pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
static inline void set_pmd_at(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp, pmd_t pmd)
|
||||||
|
{
|
||||||
|
#if PAGETABLE_LEVELS >= 3
|
||||||
|
if (sizeof(pmdval_t) > sizeof(long))
|
||||||
|
/* 5 arg words */
|
||||||
|
pv_mmu_ops.set_pmd_at(mm, addr, pmdp, pmd);
|
||||||
|
else
|
||||||
|
PVOP_VCALL4(pv_mmu_ops.set_pmd_at, mm, addr, pmdp, pmd.pmd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
|
static inline void set_pmd(pmd_t *pmdp, pmd_t pmd)
|
||||||
{
|
{
|
||||||
pmdval_t val = native_pmd_val(pmd);
|
pmdval_t val = native_pmd_val(pmd);
|
||||||
|
|
|
@ -265,10 +265,16 @@ struct pv_mmu_ops {
|
||||||
void (*set_pte_at)(struct mm_struct *mm, unsigned long addr,
|
void (*set_pte_at)(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep, pte_t pteval);
|
pte_t *ptep, pte_t pteval);
|
||||||
void (*set_pmd)(pmd_t *pmdp, pmd_t pmdval);
|
void (*set_pmd)(pmd_t *pmdp, pmd_t pmdval);
|
||||||
|
void (*set_pmd_at)(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp, pmd_t pmdval);
|
||||||
void (*pte_update)(struct mm_struct *mm, unsigned long addr,
|
void (*pte_update)(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep);
|
pte_t *ptep);
|
||||||
void (*pte_update_defer)(struct mm_struct *mm,
|
void (*pte_update_defer)(struct mm_struct *mm,
|
||||||
unsigned long addr, pte_t *ptep);
|
unsigned long addr, pte_t *ptep);
|
||||||
|
void (*pmd_update)(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp);
|
||||||
|
void (*pmd_update_defer)(struct mm_struct *mm,
|
||||||
|
unsigned long addr, pmd_t *pmdp);
|
||||||
|
|
||||||
pte_t (*ptep_modify_prot_start)(struct mm_struct *mm, unsigned long addr,
|
pte_t (*ptep_modify_prot_start)(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep);
|
pte_t *ptep);
|
||||||
|
|
|
@ -46,6 +46,15 @@ static inline pte_t native_ptep_get_and_clear(pte_t *xp)
|
||||||
#define native_ptep_get_and_clear(xp) native_local_ptep_get_and_clear(xp)
|
#define native_ptep_get_and_clear(xp) native_local_ptep_get_and_clear(xp)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp)
|
||||||
|
{
|
||||||
|
return __pmd(xchg((pmdval_t *)xp, 0));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define native_pmdp_get_and_clear(xp) native_local_pmdp_get_and_clear(xp)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bits _PAGE_BIT_PRESENT, _PAGE_BIT_FILE and _PAGE_BIT_PROTNONE are taken,
|
* Bits _PAGE_BIT_PRESENT, _PAGE_BIT_FILE and _PAGE_BIT_PROTNONE are taken,
|
||||||
* split up the 29 bits of offset into this range:
|
* split up the 29 bits of offset into this range:
|
||||||
|
|
|
@ -104,6 +104,29 @@ static inline pte_t native_ptep_get_and_clear(pte_t *ptep)
|
||||||
#define native_ptep_get_and_clear(xp) native_local_ptep_get_and_clear(xp)
|
#define native_ptep_get_and_clear(xp) native_local_ptep_get_and_clear(xp)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SMP
|
||||||
|
union split_pmd {
|
||||||
|
struct {
|
||||||
|
u32 pmd_low;
|
||||||
|
u32 pmd_high;
|
||||||
|
};
|
||||||
|
pmd_t pmd;
|
||||||
|
};
|
||||||
|
static inline pmd_t native_pmdp_get_and_clear(pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
union split_pmd res, *orig = (union split_pmd *)pmdp;
|
||||||
|
|
||||||
|
/* xchg acts as a barrier before setting of the high bits */
|
||||||
|
res.pmd_low = xchg(&orig->pmd_low, 0);
|
||||||
|
res.pmd_high = orig->pmd_high;
|
||||||
|
orig->pmd_high = 0;
|
||||||
|
|
||||||
|
return res.pmd;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define native_pmdp_get_and_clear(xp) native_local_pmdp_get_and_clear(xp)
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bits 0, 6 and 7 are taken in the low part of the pte,
|
* Bits 0, 6 and 7 are taken in the low part of the pte,
|
||||||
* put the 32 bits of offset into the high part.
|
* put the 32 bits of offset into the high part.
|
||||||
|
|
|
@ -35,6 +35,7 @@ extern struct mm_struct *pgd_page_get_mm(struct page *page);
|
||||||
#else /* !CONFIG_PARAVIRT */
|
#else /* !CONFIG_PARAVIRT */
|
||||||
#define set_pte(ptep, pte) native_set_pte(ptep, pte)
|
#define set_pte(ptep, pte) native_set_pte(ptep, pte)
|
||||||
#define set_pte_at(mm, addr, ptep, pte) native_set_pte_at(mm, addr, ptep, pte)
|
#define set_pte_at(mm, addr, ptep, pte) native_set_pte_at(mm, addr, ptep, pte)
|
||||||
|
#define set_pmd_at(mm, addr, pmdp, pmd) native_set_pmd_at(mm, addr, pmdp, pmd)
|
||||||
|
|
||||||
#define set_pte_atomic(ptep, pte) \
|
#define set_pte_atomic(ptep, pte) \
|
||||||
native_set_pte_atomic(ptep, pte)
|
native_set_pte_atomic(ptep, pte)
|
||||||
|
@ -59,6 +60,8 @@ extern struct mm_struct *pgd_page_get_mm(struct page *page);
|
||||||
|
|
||||||
#define pte_update(mm, addr, ptep) do { } while (0)
|
#define pte_update(mm, addr, ptep) do { } while (0)
|
||||||
#define pte_update_defer(mm, addr, ptep) do { } while (0)
|
#define pte_update_defer(mm, addr, ptep) do { } while (0)
|
||||||
|
#define pmd_update(mm, addr, ptep) do { } while (0)
|
||||||
|
#define pmd_update_defer(mm, addr, ptep) do { } while (0)
|
||||||
|
|
||||||
#define pgd_val(x) native_pgd_val(x)
|
#define pgd_val(x) native_pgd_val(x)
|
||||||
#define __pgd(x) native_make_pgd(x)
|
#define __pgd(x) native_make_pgd(x)
|
||||||
|
@ -94,6 +97,11 @@ static inline int pte_young(pte_t pte)
|
||||||
return pte_flags(pte) & _PAGE_ACCESSED;
|
return pte_flags(pte) & _PAGE_ACCESSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int pmd_young(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_flags(pmd) & _PAGE_ACCESSED;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int pte_write(pte_t pte)
|
static inline int pte_write(pte_t pte)
|
||||||
{
|
{
|
||||||
return pte_flags(pte) & _PAGE_RW;
|
return pte_flags(pte) & _PAGE_RW;
|
||||||
|
@ -142,6 +150,23 @@ static inline int pmd_large(pmd_t pte)
|
||||||
(_PAGE_PSE | _PAGE_PRESENT);
|
(_PAGE_PSE | _PAGE_PRESENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
static inline int pmd_trans_splitting(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_val(pmd) & _PAGE_SPLITTING;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int pmd_trans_huge(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_val(pmd) & _PAGE_PSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int has_transparent_hugepage(void)
|
||||||
|
{
|
||||||
|
return cpu_has_pse;
|
||||||
|
}
|
||||||
|
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
|
||||||
|
|
||||||
static inline pte_t pte_set_flags(pte_t pte, pteval_t set)
|
static inline pte_t pte_set_flags(pte_t pte, pteval_t set)
|
||||||
{
|
{
|
||||||
pteval_t v = native_pte_val(pte);
|
pteval_t v = native_pte_val(pte);
|
||||||
|
@ -216,6 +241,55 @@ static inline pte_t pte_mkspecial(pte_t pte)
|
||||||
return pte_set_flags(pte, _PAGE_SPECIAL);
|
return pte_set_flags(pte, _PAGE_SPECIAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_set_flags(pmd_t pmd, pmdval_t set)
|
||||||
|
{
|
||||||
|
pmdval_t v = native_pmd_val(pmd);
|
||||||
|
|
||||||
|
return __pmd(v | set);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_clear_flags(pmd_t pmd, pmdval_t clear)
|
||||||
|
{
|
||||||
|
pmdval_t v = native_pmd_val(pmd);
|
||||||
|
|
||||||
|
return __pmd(v & ~clear);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mkold(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_clear_flags(pmd, _PAGE_ACCESSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_wrprotect(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_clear_flags(pmd, _PAGE_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mkdirty(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_set_flags(pmd, _PAGE_DIRTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mkhuge(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_set_flags(pmd, _PAGE_PSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mkyoung(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_set_flags(pmd, _PAGE_ACCESSED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mkwrite(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_set_flags(pmd, _PAGE_RW);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_mknotpresent(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_clear_flags(pmd, _PAGE_PRESENT);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Mask out unsupported bits in a present pgprot. Non-present pgprots
|
* Mask out unsupported bits in a present pgprot. Non-present pgprots
|
||||||
* can use those bits for other purposes, so leave them be.
|
* can use those bits for other purposes, so leave them be.
|
||||||
|
@ -256,6 +330,16 @@ static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
|
||||||
return __pte(val);
|
return __pte(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
|
||||||
|
{
|
||||||
|
pmdval_t val = pmd_val(pmd);
|
||||||
|
|
||||||
|
val &= _HPAGE_CHG_MASK;
|
||||||
|
val |= massage_pgprot(newprot) & ~_HPAGE_CHG_MASK;
|
||||||
|
|
||||||
|
return __pmd(val);
|
||||||
|
}
|
||||||
|
|
||||||
/* mprotect needs to preserve PAT bits when updating vm_page_prot */
|
/* mprotect needs to preserve PAT bits when updating vm_page_prot */
|
||||||
#define pgprot_modify pgprot_modify
|
#define pgprot_modify pgprot_modify
|
||||||
static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
|
static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
|
||||||
|
@ -350,7 +434,7 @@ static inline unsigned long pmd_page_vaddr(pmd_t pmd)
|
||||||
* Currently stuck as a macro due to indirect forward reference to
|
* Currently stuck as a macro due to indirect forward reference to
|
||||||
* linux/mmzone.h's __section_mem_map_addr() definition:
|
* linux/mmzone.h's __section_mem_map_addr() definition:
|
||||||
*/
|
*/
|
||||||
#define pmd_page(pmd) pfn_to_page(pmd_val(pmd) >> PAGE_SHIFT)
|
#define pmd_page(pmd) pfn_to_page((pmd_val(pmd) & PTE_PFN_MASK) >> PAGE_SHIFT)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD]
|
* the pmd page can be thought of an array like this: pmd_t[PTRS_PER_PMD]
|
||||||
|
@ -524,12 +608,26 @@ static inline pte_t native_local_ptep_get_and_clear(pte_t *ptep)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline pmd_t native_local_pmdp_get_and_clear(pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
pmd_t res = *pmdp;
|
||||||
|
|
||||||
|
native_pmd_clear(pmdp);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static inline void native_set_pte_at(struct mm_struct *mm, unsigned long addr,
|
static inline void native_set_pte_at(struct mm_struct *mm, unsigned long addr,
|
||||||
pte_t *ptep , pte_t pte)
|
pte_t *ptep , pte_t pte)
|
||||||
{
|
{
|
||||||
native_set_pte(ptep, pte);
|
native_set_pte(ptep, pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void native_set_pmd_at(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp , pmd_t pmd)
|
||||||
|
{
|
||||||
|
native_set_pmd(pmdp, pmd);
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef CONFIG_PARAVIRT
|
#ifndef CONFIG_PARAVIRT
|
||||||
/*
|
/*
|
||||||
* Rules for using pte_update - it must be called after any PTE update which
|
* Rules for using pte_update - it must be called after any PTE update which
|
||||||
|
@ -607,6 +705,49 @@ static inline void ptep_set_wrprotect(struct mm_struct *mm,
|
||||||
|
|
||||||
#define flush_tlb_fix_spurious_fault(vma, address)
|
#define flush_tlb_fix_spurious_fault(vma, address)
|
||||||
|
|
||||||
|
#define mk_pmd(page, pgprot) pfn_pmd(page_to_pfn(page), (pgprot))
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_SET_ACCESS_FLAGS
|
||||||
|
extern int pmdp_set_access_flags(struct vm_area_struct *vma,
|
||||||
|
unsigned long address, pmd_t *pmdp,
|
||||||
|
pmd_t entry, int dirty);
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_TEST_AND_CLEAR_YOUNG
|
||||||
|
extern int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||||
|
unsigned long addr, pmd_t *pmdp);
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_CLEAR_YOUNG_FLUSH
|
||||||
|
extern int pmdp_clear_flush_young(struct vm_area_struct *vma,
|
||||||
|
unsigned long address, pmd_t *pmdp);
|
||||||
|
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_SPLITTING_FLUSH
|
||||||
|
extern void pmdp_splitting_flush(struct vm_area_struct *vma,
|
||||||
|
unsigned long addr, pmd_t *pmdp);
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMD_WRITE
|
||||||
|
static inline int pmd_write(pmd_t pmd)
|
||||||
|
{
|
||||||
|
return pmd_flags(pmd) & _PAGE_RW;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_GET_AND_CLEAR
|
||||||
|
static inline pmd_t pmdp_get_and_clear(struct mm_struct *mm, unsigned long addr,
|
||||||
|
pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
pmd_t pmd = native_pmdp_get_and_clear(pmdp);
|
||||||
|
pmd_update(mm, addr, pmdp);
|
||||||
|
return pmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_PMDP_SET_WRPROTECT
|
||||||
|
static inline void pmdp_set_wrprotect(struct mm_struct *mm,
|
||||||
|
unsigned long addr, pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
clear_bit(_PAGE_BIT_RW, (unsigned long *)pmdp);
|
||||||
|
pmd_update(mm, addr, pmdp);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* clone_pgd_range(pgd_t *dst, pgd_t *src, int count);
|
* clone_pgd_range(pgd_t *dst, pgd_t *src, int count);
|
||||||
*
|
*
|
||||||
|
|
|
@ -59,6 +59,16 @@ static inline void native_set_pte_atomic(pte_t *ptep, pte_t pte)
|
||||||
native_set_pte(ptep, pte);
|
native_set_pte(ptep, pte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
|
||||||
|
{
|
||||||
|
*pmdp = pmd;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void native_pmd_clear(pmd_t *pmd)
|
||||||
|
{
|
||||||
|
native_set_pmd(pmd, native_make_pmd(0));
|
||||||
|
}
|
||||||
|
|
||||||
static inline pte_t native_ptep_get_and_clear(pte_t *xp)
|
static inline pte_t native_ptep_get_and_clear(pte_t *xp)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_SMP
|
#ifdef CONFIG_SMP
|
||||||
|
@ -72,14 +82,17 @@ static inline pte_t native_ptep_get_and_clear(pte_t *xp)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void native_set_pmd(pmd_t *pmdp, pmd_t pmd)
|
static inline pmd_t native_pmdp_get_and_clear(pmd_t *xp)
|
||||||
{
|
{
|
||||||
*pmdp = pmd;
|
#ifdef CONFIG_SMP
|
||||||
}
|
return native_make_pmd(xchg(&xp->pmd, 0));
|
||||||
|
#else
|
||||||
static inline void native_pmd_clear(pmd_t *pmd)
|
/* native_local_pmdp_get_and_clear,
|
||||||
{
|
but duplicated because of cyclic dependency */
|
||||||
native_set_pmd(pmd, native_make_pmd(0));
|
pmd_t ret = *xp;
|
||||||
|
native_pmd_clear(xp);
|
||||||
|
return ret;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void native_set_pud(pud_t *pudp, pud_t pud)
|
static inline void native_set_pud(pud_t *pudp, pud_t pud)
|
||||||
|
@ -168,6 +181,7 @@ extern void cleanup_highmap(void);
|
||||||
#define kc_offset_to_vaddr(o) ((o) | ~__VIRTUAL_MASK)
|
#define kc_offset_to_vaddr(o) ((o) | ~__VIRTUAL_MASK)
|
||||||
|
|
||||||
#define __HAVE_ARCH_PTE_SAME
|
#define __HAVE_ARCH_PTE_SAME
|
||||||
|
|
||||||
#endif /* !__ASSEMBLY__ */
|
#endif /* !__ASSEMBLY__ */
|
||||||
|
|
||||||
#endif /* _ASM_X86_PGTABLE_64_H */
|
#endif /* _ASM_X86_PGTABLE_64_H */
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
|
#define _PAGE_BIT_PAT_LARGE 12 /* On 2MB or 1GB pages */
|
||||||
#define _PAGE_BIT_SPECIAL _PAGE_BIT_UNUSED1
|
#define _PAGE_BIT_SPECIAL _PAGE_BIT_UNUSED1
|
||||||
#define _PAGE_BIT_CPA_TEST _PAGE_BIT_UNUSED1
|
#define _PAGE_BIT_CPA_TEST _PAGE_BIT_UNUSED1
|
||||||
|
#define _PAGE_BIT_SPLITTING _PAGE_BIT_UNUSED1 /* only valid on a PSE pmd */
|
||||||
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
|
#define _PAGE_BIT_NX 63 /* No execute: only valid after cpuid check */
|
||||||
|
|
||||||
/* If _PAGE_BIT_PRESENT is clear, we use these: */
|
/* If _PAGE_BIT_PRESENT is clear, we use these: */
|
||||||
|
@ -45,6 +46,7 @@
|
||||||
#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
|
#define _PAGE_PAT_LARGE (_AT(pteval_t, 1) << _PAGE_BIT_PAT_LARGE)
|
||||||
#define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)
|
#define _PAGE_SPECIAL (_AT(pteval_t, 1) << _PAGE_BIT_SPECIAL)
|
||||||
#define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST)
|
#define _PAGE_CPA_TEST (_AT(pteval_t, 1) << _PAGE_BIT_CPA_TEST)
|
||||||
|
#define _PAGE_SPLITTING (_AT(pteval_t, 1) << _PAGE_BIT_SPLITTING)
|
||||||
#define __HAVE_ARCH_PTE_SPECIAL
|
#define __HAVE_ARCH_PTE_SPECIAL
|
||||||
|
|
||||||
#ifdef CONFIG_KMEMCHECK
|
#ifdef CONFIG_KMEMCHECK
|
||||||
|
@ -70,6 +72,7 @@
|
||||||
/* Set of bits not changed in pte_modify */
|
/* Set of bits not changed in pte_modify */
|
||||||
#define _PAGE_CHG_MASK (PTE_PFN_MASK | _PAGE_PCD | _PAGE_PWT | \
|
#define _PAGE_CHG_MASK (PTE_PFN_MASK | _PAGE_PCD | _PAGE_PWT | \
|
||||||
_PAGE_SPECIAL | _PAGE_ACCESSED | _PAGE_DIRTY)
|
_PAGE_SPECIAL | _PAGE_ACCESSED | _PAGE_DIRTY)
|
||||||
|
#define _HPAGE_CHG_MASK (_PAGE_CHG_MASK | _PAGE_PSE)
|
||||||
|
|
||||||
#define _PAGE_CACHE_MASK (_PAGE_PCD | _PAGE_PWT)
|
#define _PAGE_CACHE_MASK (_PAGE_PCD | _PAGE_PWT)
|
||||||
#define _PAGE_CACHE_WB (0)
|
#define _PAGE_CACHE_WB (0)
|
||||||
|
|
|
@ -42,6 +42,11 @@ extern unsigned int machine_to_phys_order;
|
||||||
extern unsigned long get_phys_to_machine(unsigned long pfn);
|
extern unsigned long get_phys_to_machine(unsigned long pfn);
|
||||||
extern bool set_phys_to_machine(unsigned long pfn, unsigned long mfn);
|
extern bool set_phys_to_machine(unsigned long pfn, unsigned long mfn);
|
||||||
|
|
||||||
|
extern int m2p_add_override(unsigned long mfn, struct page *page);
|
||||||
|
extern int m2p_remove_override(struct page *page);
|
||||||
|
extern struct page *m2p_find_override(unsigned long mfn);
|
||||||
|
extern unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn);
|
||||||
|
|
||||||
static inline unsigned long pfn_to_mfn(unsigned long pfn)
|
static inline unsigned long pfn_to_mfn(unsigned long pfn)
|
||||||
{
|
{
|
||||||
unsigned long mfn;
|
unsigned long mfn;
|
||||||
|
@ -72,9 +77,6 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn)
|
||||||
if (xen_feature(XENFEAT_auto_translated_physmap))
|
if (xen_feature(XENFEAT_auto_translated_physmap))
|
||||||
return mfn;
|
return mfn;
|
||||||
|
|
||||||
if (unlikely((mfn >> machine_to_phys_order) != 0))
|
|
||||||
return ~0;
|
|
||||||
|
|
||||||
pfn = 0;
|
pfn = 0;
|
||||||
/*
|
/*
|
||||||
* The array access can fail (e.g., device space beyond end of RAM).
|
* The array access can fail (e.g., device space beyond end of RAM).
|
||||||
|
@ -83,6 +85,14 @@ static inline unsigned long mfn_to_pfn(unsigned long mfn)
|
||||||
*/
|
*/
|
||||||
__get_user(pfn, &machine_to_phys_mapping[mfn]);
|
__get_user(pfn, &machine_to_phys_mapping[mfn]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this appears to be a foreign mfn (because the pfn
|
||||||
|
* doesn't map back to the mfn), then check the local override
|
||||||
|
* table to see if there's a better pfn to use.
|
||||||
|
*/
|
||||||
|
if (get_phys_to_machine(pfn) != mfn)
|
||||||
|
pfn = m2p_find_override_pfn(mfn, pfn);
|
||||||
|
|
||||||
return pfn;
|
return pfn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,20 +37,11 @@
|
||||||
|
|
||||||
void *module_alloc(unsigned long size)
|
void *module_alloc(unsigned long size)
|
||||||
{
|
{
|
||||||
struct vm_struct *area;
|
if (PAGE_ALIGN(size) > MODULES_LEN)
|
||||||
|
|
||||||
if (!size)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
size = PAGE_ALIGN(size);
|
return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
|
||||||
if (size > MODULES_LEN)
|
GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL_EXEC,
|
||||||
return NULL;
|
-1, __builtin_return_address(0));
|
||||||
|
|
||||||
area = __get_vm_area(size, VM_ALLOC, MODULES_VADDR, MODULES_END);
|
|
||||||
if (!area)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
return __vmalloc_area(area, GFP_KERNEL | __GFP_HIGHMEM,
|
|
||||||
PAGE_KERNEL_EXEC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Free memory returned from module_alloc */
|
/* Free memory returned from module_alloc */
|
||||||
|
|
|
@ -421,8 +421,11 @@ struct pv_mmu_ops pv_mmu_ops = {
|
||||||
.set_pte = native_set_pte,
|
.set_pte = native_set_pte,
|
||||||
.set_pte_at = native_set_pte_at,
|
.set_pte_at = native_set_pte_at,
|
||||||
.set_pmd = native_set_pmd,
|
.set_pmd = native_set_pmd,
|
||||||
|
.set_pmd_at = native_set_pmd_at,
|
||||||
.pte_update = paravirt_nop,
|
.pte_update = paravirt_nop,
|
||||||
.pte_update_defer = paravirt_nop,
|
.pte_update_defer = paravirt_nop,
|
||||||
|
.pmd_update = paravirt_nop,
|
||||||
|
.pmd_update_defer = paravirt_nop,
|
||||||
|
|
||||||
.ptep_modify_prot_start = __ptep_modify_prot_start,
|
.ptep_modify_prot_start = __ptep_modify_prot_start,
|
||||||
.ptep_modify_prot_commit = __ptep_modify_prot_commit,
|
.ptep_modify_prot_commit = __ptep_modify_prot_commit,
|
||||||
|
|
|
@ -133,7 +133,7 @@ static int map_tboot_page(unsigned long vaddr, unsigned long pfn,
|
||||||
pmd = pmd_alloc(&tboot_mm, pud, vaddr);
|
pmd = pmd_alloc(&tboot_mm, pud, vaddr);
|
||||||
if (!pmd)
|
if (!pmd)
|
||||||
return -1;
|
return -1;
|
||||||
pte = pte_alloc_map(&tboot_mm, pmd, vaddr);
|
pte = pte_alloc_map(&tboot_mm, NULL, pmd, vaddr);
|
||||||
if (!pte)
|
if (!pte)
|
||||||
return -1;
|
return -1;
|
||||||
set_pte_at(&tboot_mm, vaddr, pte, pfn_pte(pfn, prot));
|
set_pte_at(&tboot_mm, vaddr, pte, pfn_pte(pfn, prot));
|
||||||
|
|
|
@ -179,6 +179,7 @@ static void mark_screen_rdonly(struct mm_struct *mm)
|
||||||
if (pud_none_or_clear_bad(pud))
|
if (pud_none_or_clear_bad(pud))
|
||||||
goto out;
|
goto out;
|
||||||
pmd = pmd_offset(pud, 0xA0000);
|
pmd = pmd_offset(pud, 0xA0000);
|
||||||
|
split_huge_page_pmd(mm, pmd);
|
||||||
if (pmd_none_or_clear_bad(pmd))
|
if (pmd_none_or_clear_bad(pmd))
|
||||||
goto out;
|
goto out;
|
||||||
pte = pte_offset_map_lock(mm, pmd, 0xA0000, &ptl);
|
pte = pte_offset_map_lock(mm, pmd, 0xA0000, &ptl);
|
||||||
|
|
|
@ -554,14 +554,18 @@ static int host_mapping_level(struct kvm *kvm, gfn_t gfn)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
|
static bool mapping_level_dirty_bitmap(struct kvm_vcpu *vcpu, gfn_t large_gfn)
|
||||||
{
|
{
|
||||||
struct kvm_memory_slot *slot;
|
struct kvm_memory_slot *slot;
|
||||||
int host_level, level, max_level;
|
|
||||||
|
|
||||||
slot = gfn_to_memslot(vcpu->kvm, large_gfn);
|
slot = gfn_to_memslot(vcpu->kvm, large_gfn);
|
||||||
if (slot && slot->dirty_bitmap)
|
if (slot && slot->dirty_bitmap)
|
||||||
return PT_PAGE_TABLE_LEVEL;
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mapping_level(struct kvm_vcpu *vcpu, gfn_t large_gfn)
|
||||||
|
{
|
||||||
|
int host_level, level, max_level;
|
||||||
|
|
||||||
host_level = host_mapping_level(vcpu->kvm, large_gfn);
|
host_level = host_mapping_level(vcpu->kvm, large_gfn);
|
||||||
|
|
||||||
|
@ -941,6 +945,35 @@ static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
|
||||||
return young;
|
return young;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int kvm_test_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
|
||||||
|
unsigned long data)
|
||||||
|
{
|
||||||
|
u64 *spte;
|
||||||
|
int young = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If there's no access bit in the secondary pte set by the
|
||||||
|
* hardware it's up to gup-fast/gup to set the access bit in
|
||||||
|
* the primary pte or in the page structure.
|
||||||
|
*/
|
||||||
|
if (!shadow_accessed_mask)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
spte = rmap_next(kvm, rmapp, NULL);
|
||||||
|
while (spte) {
|
||||||
|
u64 _spte = *spte;
|
||||||
|
BUG_ON(!(_spte & PT_PRESENT_MASK));
|
||||||
|
young = _spte & PT_ACCESSED_MASK;
|
||||||
|
if (young) {
|
||||||
|
young = 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spte = rmap_next(kvm, rmapp, spte);
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return young;
|
||||||
|
}
|
||||||
|
|
||||||
#define RMAP_RECYCLE_THRESHOLD 1000
|
#define RMAP_RECYCLE_THRESHOLD 1000
|
||||||
|
|
||||||
static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
|
static void rmap_recycle(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn)
|
||||||
|
@ -961,6 +994,11 @@ int kvm_age_hva(struct kvm *kvm, unsigned long hva)
|
||||||
return kvm_handle_hva(kvm, hva, 0, kvm_age_rmapp);
|
return kvm_handle_hva(kvm, hva, 0, kvm_age_rmapp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva)
|
||||||
|
{
|
||||||
|
return kvm_handle_hva(kvm, hva, 0, kvm_test_age_rmapp);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef MMU_DEBUG
|
#ifdef MMU_DEBUG
|
||||||
static int is_empty_shadow_page(u64 *spt)
|
static int is_empty_shadow_page(u64 *spt)
|
||||||
{
|
{
|
||||||
|
@ -2281,6 +2319,48 @@ static int kvm_handle_bad_page(struct kvm *kvm, gfn_t gfn, pfn_t pfn)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void transparent_hugepage_adjust(struct kvm_vcpu *vcpu,
|
||||||
|
gfn_t *gfnp, pfn_t *pfnp, int *levelp)
|
||||||
|
{
|
||||||
|
pfn_t pfn = *pfnp;
|
||||||
|
gfn_t gfn = *gfnp;
|
||||||
|
int level = *levelp;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if it's a transparent hugepage. If this would be an
|
||||||
|
* hugetlbfs page, level wouldn't be set to
|
||||||
|
* PT_PAGE_TABLE_LEVEL and there would be no adjustment done
|
||||||
|
* here.
|
||||||
|
*/
|
||||||
|
if (!is_error_pfn(pfn) && !kvm_is_mmio_pfn(pfn) &&
|
||||||
|
level == PT_PAGE_TABLE_LEVEL &&
|
||||||
|
PageTransCompound(pfn_to_page(pfn)) &&
|
||||||
|
!has_wrprotected_page(vcpu->kvm, gfn, PT_DIRECTORY_LEVEL)) {
|
||||||
|
unsigned long mask;
|
||||||
|
/*
|
||||||
|
* mmu_notifier_retry was successful and we hold the
|
||||||
|
* mmu_lock here, so the pmd can't become splitting
|
||||||
|
* from under us, and in turn
|
||||||
|
* __split_huge_page_refcount() can't run from under
|
||||||
|
* us and we can safely transfer the refcount from
|
||||||
|
* PG_tail to PG_head as we switch the pfn to tail to
|
||||||
|
* head.
|
||||||
|
*/
|
||||||
|
*levelp = level = PT_DIRECTORY_LEVEL;
|
||||||
|
mask = KVM_PAGES_PER_HPAGE(level) - 1;
|
||||||
|
VM_BUG_ON((gfn & mask) != (pfn & mask));
|
||||||
|
if (pfn & mask) {
|
||||||
|
gfn &= ~mask;
|
||||||
|
*gfnp = gfn;
|
||||||
|
kvm_release_pfn_clean(pfn);
|
||||||
|
pfn &= ~mask;
|
||||||
|
if (!get_page_unless_zero(pfn_to_page(pfn)))
|
||||||
|
BUG();
|
||||||
|
*pfnp = pfn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
|
static bool try_async_pf(struct kvm_vcpu *vcpu, bool prefault, gfn_t gfn,
|
||||||
gva_t gva, pfn_t *pfn, bool write, bool *writable);
|
gva_t gva, pfn_t *pfn, bool write, bool *writable);
|
||||||
|
|
||||||
|
@ -2289,20 +2369,25 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn,
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
int level;
|
int level;
|
||||||
|
int force_pt_level;
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
unsigned long mmu_seq;
|
unsigned long mmu_seq;
|
||||||
bool map_writable;
|
bool map_writable;
|
||||||
|
|
||||||
level = mapping_level(vcpu, gfn);
|
force_pt_level = mapping_level_dirty_bitmap(vcpu, gfn);
|
||||||
|
if (likely(!force_pt_level)) {
|
||||||
|
level = mapping_level(vcpu, gfn);
|
||||||
|
/*
|
||||||
|
* This path builds a PAE pagetable - so we can map
|
||||||
|
* 2mb pages at maximum. Therefore check if the level
|
||||||
|
* is larger than that.
|
||||||
|
*/
|
||||||
|
if (level > PT_DIRECTORY_LEVEL)
|
||||||
|
level = PT_DIRECTORY_LEVEL;
|
||||||
|
|
||||||
/*
|
gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
||||||
* This path builds a PAE pagetable - so we can map 2mb pages at
|
} else
|
||||||
* maximum. Therefore check if the level is larger than that.
|
level = PT_PAGE_TABLE_LEVEL;
|
||||||
*/
|
|
||||||
if (level > PT_DIRECTORY_LEVEL)
|
|
||||||
level = PT_DIRECTORY_LEVEL;
|
|
||||||
|
|
||||||
gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
|
||||||
|
|
||||||
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
|
@ -2318,6 +2403,8 @@ static int nonpaging_map(struct kvm_vcpu *vcpu, gva_t v, int write, gfn_t gfn,
|
||||||
if (mmu_notifier_retry(vcpu, mmu_seq))
|
if (mmu_notifier_retry(vcpu, mmu_seq))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
|
if (likely(!force_pt_level))
|
||||||
|
transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
|
||||||
r = __direct_map(vcpu, v, write, map_writable, level, gfn, pfn,
|
r = __direct_map(vcpu, v, write, map_writable, level, gfn, pfn,
|
||||||
prefault);
|
prefault);
|
||||||
spin_unlock(&vcpu->kvm->mmu_lock);
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
@ -2655,6 +2742,7 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
int r;
|
int r;
|
||||||
int level;
|
int level;
|
||||||
|
int force_pt_level;
|
||||||
gfn_t gfn = gpa >> PAGE_SHIFT;
|
gfn_t gfn = gpa >> PAGE_SHIFT;
|
||||||
unsigned long mmu_seq;
|
unsigned long mmu_seq;
|
||||||
int write = error_code & PFERR_WRITE_MASK;
|
int write = error_code & PFERR_WRITE_MASK;
|
||||||
|
@ -2667,9 +2755,12 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
|
||||||
if (r)
|
if (r)
|
||||||
return r;
|
return r;
|
||||||
|
|
||||||
level = mapping_level(vcpu, gfn);
|
force_pt_level = mapping_level_dirty_bitmap(vcpu, gfn);
|
||||||
|
if (likely(!force_pt_level)) {
|
||||||
gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
level = mapping_level(vcpu, gfn);
|
||||||
|
gfn &= ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
||||||
|
} else
|
||||||
|
level = PT_PAGE_TABLE_LEVEL;
|
||||||
|
|
||||||
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
mmu_seq = vcpu->kvm->mmu_notifier_seq;
|
||||||
smp_rmb();
|
smp_rmb();
|
||||||
|
@ -2684,6 +2775,8 @@ static int tdp_page_fault(struct kvm_vcpu *vcpu, gva_t gpa, u32 error_code,
|
||||||
if (mmu_notifier_retry(vcpu, mmu_seq))
|
if (mmu_notifier_retry(vcpu, mmu_seq))
|
||||||
goto out_unlock;
|
goto out_unlock;
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
|
if (likely(!force_pt_level))
|
||||||
|
transparent_hugepage_adjust(vcpu, &gfn, &pfn, &level);
|
||||||
r = __direct_map(vcpu, gpa, write, map_writable,
|
r = __direct_map(vcpu, gpa, write, map_writable,
|
||||||
level, gfn, pfn, prefault);
|
level, gfn, pfn, prefault);
|
||||||
spin_unlock(&vcpu->kvm->mmu_lock);
|
spin_unlock(&vcpu->kvm->mmu_lock);
|
||||||
|
|
|
@ -550,6 +550,7 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
|
||||||
int r;
|
int r;
|
||||||
pfn_t pfn;
|
pfn_t pfn;
|
||||||
int level = PT_PAGE_TABLE_LEVEL;
|
int level = PT_PAGE_TABLE_LEVEL;
|
||||||
|
int force_pt_level;
|
||||||
unsigned long mmu_seq;
|
unsigned long mmu_seq;
|
||||||
bool map_writable;
|
bool map_writable;
|
||||||
|
|
||||||
|
@ -577,7 +578,11 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (walker.level >= PT_DIRECTORY_LEVEL) {
|
if (walker.level >= PT_DIRECTORY_LEVEL)
|
||||||
|
force_pt_level = mapping_level_dirty_bitmap(vcpu, walker.gfn);
|
||||||
|
else
|
||||||
|
force_pt_level = 1;
|
||||||
|
if (!force_pt_level) {
|
||||||
level = min(walker.level, mapping_level(vcpu, walker.gfn));
|
level = min(walker.level, mapping_level(vcpu, walker.gfn));
|
||||||
walker.gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
walker.gfn = walker.gfn & ~(KVM_PAGES_PER_HPAGE(level) - 1);
|
||||||
}
|
}
|
||||||
|
@ -599,6 +604,8 @@ static int FNAME(page_fault)(struct kvm_vcpu *vcpu, gva_t addr, u32 error_code,
|
||||||
|
|
||||||
trace_kvm_mmu_audit(vcpu, AUDIT_PRE_PAGE_FAULT);
|
trace_kvm_mmu_audit(vcpu, AUDIT_PRE_PAGE_FAULT);
|
||||||
kvm_mmu_free_some_pages(vcpu);
|
kvm_mmu_free_some_pages(vcpu);
|
||||||
|
if (!force_pt_level)
|
||||||
|
transparent_hugepage_adjust(vcpu, &walker.gfn, &pfn, &level);
|
||||||
sptep = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault,
|
sptep = FNAME(fetch)(vcpu, addr, &walker, user_fault, write_fault,
|
||||||
level, &write_pt, pfn, map_writable, prefault);
|
level, &write_pt, pfn, map_writable, prefault);
|
||||||
(void)sptep;
|
(void)sptep;
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <linux/mm.h>
|
#include <linux/mm.h>
|
||||||
#include <linux/vmstat.h>
|
#include <linux/vmstat.h>
|
||||||
#include <linux/highmem.h>
|
#include <linux/highmem.h>
|
||||||
|
#include <linux/swap.h>
|
||||||
|
|
||||||
#include <asm/pgtable.h>
|
#include <asm/pgtable.h>
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ static noinline int gup_pte_range(pmd_t pmd, unsigned long addr,
|
||||||
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
|
VM_BUG_ON(!pfn_valid(pte_pfn(pte)));
|
||||||
page = pte_page(pte);
|
page = pte_page(pte);
|
||||||
get_page(page);
|
get_page(page);
|
||||||
|
SetPageReferenced(page);
|
||||||
pages[*nr] = page;
|
pages[*nr] = page;
|
||||||
(*nr)++;
|
(*nr)++;
|
||||||
|
|
||||||
|
@ -103,6 +105,17 @@ static inline void get_head_page_multiple(struct page *page, int nr)
|
||||||
VM_BUG_ON(page != compound_head(page));
|
VM_BUG_ON(page != compound_head(page));
|
||||||
VM_BUG_ON(page_count(page) == 0);
|
VM_BUG_ON(page_count(page) == 0);
|
||||||
atomic_add(nr, &page->_count);
|
atomic_add(nr, &page->_count);
|
||||||
|
SetPageReferenced(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void get_huge_page_tail(struct page *page)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* __split_huge_page_refcount() cannot run
|
||||||
|
* from under us.
|
||||||
|
*/
|
||||||
|
VM_BUG_ON(atomic_read(&page->_count) < 0);
|
||||||
|
atomic_inc(&page->_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
|
static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
|
||||||
|
@ -128,6 +141,8 @@ static noinline int gup_huge_pmd(pmd_t pmd, unsigned long addr,
|
||||||
do {
|
do {
|
||||||
VM_BUG_ON(compound_head(page) != head);
|
VM_BUG_ON(compound_head(page) != head);
|
||||||
pages[*nr] = page;
|
pages[*nr] = page;
|
||||||
|
if (PageTail(page))
|
||||||
|
get_huge_page_tail(page);
|
||||||
(*nr)++;
|
(*nr)++;
|
||||||
page++;
|
page++;
|
||||||
refs++;
|
refs++;
|
||||||
|
@ -148,7 +163,18 @@ static int gup_pmd_range(pud_t pud, unsigned long addr, unsigned long end,
|
||||||
pmd_t pmd = *pmdp;
|
pmd_t pmd = *pmdp;
|
||||||
|
|
||||||
next = pmd_addr_end(addr, end);
|
next = pmd_addr_end(addr, end);
|
||||||
if (pmd_none(pmd))
|
/*
|
||||||
|
* The pmd_trans_splitting() check below explains why
|
||||||
|
* pmdp_splitting_flush has to flush the tlb, to stop
|
||||||
|
* this gup-fast code from running while we set the
|
||||||
|
* splitting bit in the pmd. Returning zero will take
|
||||||
|
* the slow path that will call wait_split_huge_page()
|
||||||
|
* if the pmd is still in splitting state. gup-fast
|
||||||
|
* can't because it has irq disabled and
|
||||||
|
* wait_split_huge_page() would never return as the
|
||||||
|
* tlb flush IPI wouldn't run.
|
||||||
|
*/
|
||||||
|
if (pmd_none(pmd) || pmd_trans_splitting(pmd))
|
||||||
return 0;
|
return 0;
|
||||||
if (unlikely(pmd_large(pmd))) {
|
if (unlikely(pmd_large(pmd))) {
|
||||||
if (!gup_huge_pmd(pmd, addr, next, write, pages, nr))
|
if (!gup_huge_pmd(pmd, addr, next, write, pages, nr))
|
||||||
|
|
|
@ -320,6 +320,25 @@ int ptep_set_access_flags(struct vm_area_struct *vma,
|
||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
int pmdp_set_access_flags(struct vm_area_struct *vma,
|
||||||
|
unsigned long address, pmd_t *pmdp,
|
||||||
|
pmd_t entry, int dirty)
|
||||||
|
{
|
||||||
|
int changed = !pmd_same(*pmdp, entry);
|
||||||
|
|
||||||
|
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
|
||||||
|
|
||||||
|
if (changed && dirty) {
|
||||||
|
*pmdp = entry;
|
||||||
|
pmd_update_defer(vma->vm_mm, address, pmdp);
|
||||||
|
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changed;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||||
unsigned long addr, pte_t *ptep)
|
unsigned long addr, pte_t *ptep)
|
||||||
{
|
{
|
||||||
|
@ -335,6 +354,23 @@ int ptep_test_and_clear_young(struct vm_area_struct *vma,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
int pmdp_test_and_clear_young(struct vm_area_struct *vma,
|
||||||
|
unsigned long addr, pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (pmd_young(*pmdp))
|
||||||
|
ret = test_and_clear_bit(_PAGE_BIT_ACCESSED,
|
||||||
|
(unsigned long *)pmdp);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
pmd_update(vma->vm_mm, addr, pmdp);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int ptep_clear_flush_young(struct vm_area_struct *vma,
|
int ptep_clear_flush_young(struct vm_area_struct *vma,
|
||||||
unsigned long address, pte_t *ptep)
|
unsigned long address, pte_t *ptep)
|
||||||
{
|
{
|
||||||
|
@ -347,6 +383,36 @@ int ptep_clear_flush_young(struct vm_area_struct *vma,
|
||||||
return young;
|
return young;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
int pmdp_clear_flush_young(struct vm_area_struct *vma,
|
||||||
|
unsigned long address, pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
int young;
|
||||||
|
|
||||||
|
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
|
||||||
|
|
||||||
|
young = pmdp_test_and_clear_young(vma, address, pmdp);
|
||||||
|
if (young)
|
||||||
|
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
|
||||||
|
|
||||||
|
return young;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pmdp_splitting_flush(struct vm_area_struct *vma,
|
||||||
|
unsigned long address, pmd_t *pmdp)
|
||||||
|
{
|
||||||
|
int set;
|
||||||
|
VM_BUG_ON(address & ~HPAGE_PMD_MASK);
|
||||||
|
set = !test_and_set_bit(_PAGE_BIT_SPLITTING,
|
||||||
|
(unsigned long *)pmdp);
|
||||||
|
if (set) {
|
||||||
|
pmd_update(vma->vm_mm, address, pmdp);
|
||||||
|
/* need tlb flush only to serialize against gup-fast */
|
||||||
|
flush_tlb_range(vma, address, address + HPAGE_PMD_SIZE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* reserve_top_address - reserves a hole in the top of kernel address space
|
* reserve_top_address - reserves a hole in the top of kernel address space
|
||||||
* @reserve - size of hole to reserve
|
* @reserve - size of hole to reserve
|
||||||
|
|
|
@ -12,7 +12,8 @@ CFLAGS_mmu.o := $(nostackp)
|
||||||
|
|
||||||
obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \
|
obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \
|
||||||
time.o xen-asm.o xen-asm_$(BITS).o \
|
time.o xen-asm.o xen-asm_$(BITS).o \
|
||||||
grant-table.o suspend.o platform-pci-unplug.o
|
grant-table.o suspend.o platform-pci-unplug.o \
|
||||||
|
p2m.o
|
||||||
|
|
||||||
obj-$(CONFIG_SMP) += smp.o
|
obj-$(CONFIG_SMP) += smp.o
|
||||||
obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
|
obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o
|
||||||
|
|
|
@ -173,371 +173,6 @@ DEFINE_PER_CPU(unsigned long, xen_current_cr3); /* actual vcpu cr3 */
|
||||||
*/
|
*/
|
||||||
#define USER_LIMIT ((STACK_TOP_MAX + PGDIR_SIZE - 1) & PGDIR_MASK)
|
#define USER_LIMIT ((STACK_TOP_MAX + PGDIR_SIZE - 1) & PGDIR_MASK)
|
||||||
|
|
||||||
/*
|
|
||||||
* Xen leaves the responsibility for maintaining p2m mappings to the
|
|
||||||
* guests themselves, but it must also access and update the p2m array
|
|
||||||
* during suspend/resume when all the pages are reallocated.
|
|
||||||
*
|
|
||||||
* The p2m table is logically a flat array, but we implement it as a
|
|
||||||
* three-level tree to allow the address space to be sparse.
|
|
||||||
*
|
|
||||||
* Xen
|
|
||||||
* |
|
|
||||||
* p2m_top p2m_top_mfn
|
|
||||||
* / \ / \
|
|
||||||
* p2m_mid p2m_mid p2m_mid_mfn p2m_mid_mfn
|
|
||||||
* / \ / \ / /
|
|
||||||
* p2m p2m p2m p2m p2m p2m p2m ...
|
|
||||||
*
|
|
||||||
* The p2m_mid_mfn pages are mapped by p2m_top_mfn_p.
|
|
||||||
*
|
|
||||||
* The p2m_top and p2m_top_mfn levels are limited to 1 page, so the
|
|
||||||
* maximum representable pseudo-physical address space is:
|
|
||||||
* P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE pages
|
|
||||||
*
|
|
||||||
* P2M_PER_PAGE depends on the architecture, as a mfn is always
|
|
||||||
* unsigned long (8 bytes on 64-bit, 4 bytes on 32), leading to
|
|
||||||
* 512 and 1024 entries respectively.
|
|
||||||
*/
|
|
||||||
|
|
||||||
unsigned long xen_max_p2m_pfn __read_mostly;
|
|
||||||
|
|
||||||
#define P2M_PER_PAGE (PAGE_SIZE / sizeof(unsigned long))
|
|
||||||
#define P2M_MID_PER_PAGE (PAGE_SIZE / sizeof(unsigned long *))
|
|
||||||
#define P2M_TOP_PER_PAGE (PAGE_SIZE / sizeof(unsigned long **))
|
|
||||||
|
|
||||||
#define MAX_P2M_PFN (P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE)
|
|
||||||
|
|
||||||
/* Placeholders for holes in the address space */
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long, p2m_missing, P2M_PER_PAGE);
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long *, p2m_mid_missing, P2M_MID_PER_PAGE);
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long, p2m_mid_missing_mfn, P2M_MID_PER_PAGE);
|
|
||||||
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long **, p2m_top, P2M_TOP_PER_PAGE);
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long, p2m_top_mfn, P2M_TOP_PER_PAGE);
|
|
||||||
static RESERVE_BRK_ARRAY(unsigned long *, p2m_top_mfn_p, P2M_TOP_PER_PAGE);
|
|
||||||
|
|
||||||
RESERVE_BRK(p2m_mid, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
|
|
||||||
RESERVE_BRK(p2m_mid_mfn, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
|
|
||||||
|
|
||||||
static inline unsigned p2m_top_index(unsigned long pfn)
|
|
||||||
{
|
|
||||||
BUG_ON(pfn >= MAX_P2M_PFN);
|
|
||||||
return pfn / (P2M_MID_PER_PAGE * P2M_PER_PAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned p2m_mid_index(unsigned long pfn)
|
|
||||||
{
|
|
||||||
return (pfn / P2M_PER_PAGE) % P2M_MID_PER_PAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline unsigned p2m_index(unsigned long pfn)
|
|
||||||
{
|
|
||||||
return pfn % P2M_PER_PAGE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_top_init(unsigned long ***top)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
|
||||||
top[i] = p2m_mid_missing;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_top_mfn_init(unsigned long *top)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
|
||||||
top[i] = virt_to_mfn(p2m_mid_missing_mfn);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_top_mfn_p_init(unsigned long **top)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
|
||||||
top[i] = p2m_mid_missing_mfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_mid_init(unsigned long **mid)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
|
||||||
mid[i] = p2m_missing;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_mid_mfn_init(unsigned long *mid)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
|
||||||
mid[i] = virt_to_mfn(p2m_missing);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void p2m_init(unsigned long *p2m)
|
|
||||||
{
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
|
||||||
p2m[i] = INVALID_P2M_ENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Build the parallel p2m_top_mfn and p2m_mid_mfn structures
|
|
||||||
*
|
|
||||||
* This is called both at boot time, and after resuming from suspend:
|
|
||||||
* - At boot time we're called very early, and must use extend_brk()
|
|
||||||
* to allocate memory.
|
|
||||||
*
|
|
||||||
* - After resume we're called from within stop_machine, but the mfn
|
|
||||||
* tree should alreay be completely allocated.
|
|
||||||
*/
|
|
||||||
void xen_build_mfn_list_list(void)
|
|
||||||
{
|
|
||||||
unsigned long pfn;
|
|
||||||
|
|
||||||
/* Pre-initialize p2m_top_mfn to be completely missing */
|
|
||||||
if (p2m_top_mfn == NULL) {
|
|
||||||
p2m_mid_missing_mfn = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_mid_mfn_init(p2m_mid_missing_mfn);
|
|
||||||
|
|
||||||
p2m_top_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_top_mfn_p_init(p2m_top_mfn_p);
|
|
||||||
|
|
||||||
p2m_top_mfn = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_top_mfn_init(p2m_top_mfn);
|
|
||||||
} else {
|
|
||||||
/* Reinitialise, mfn's all change after migration */
|
|
||||||
p2m_mid_mfn_init(p2m_mid_missing_mfn);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (pfn = 0; pfn < xen_max_p2m_pfn; pfn += P2M_PER_PAGE) {
|
|
||||||
unsigned topidx = p2m_top_index(pfn);
|
|
||||||
unsigned mididx = p2m_mid_index(pfn);
|
|
||||||
unsigned long **mid;
|
|
||||||
unsigned long *mid_mfn_p;
|
|
||||||
|
|
||||||
mid = p2m_top[topidx];
|
|
||||||
mid_mfn_p = p2m_top_mfn_p[topidx];
|
|
||||||
|
|
||||||
/* Don't bother allocating any mfn mid levels if
|
|
||||||
* they're just missing, just update the stored mfn,
|
|
||||||
* since all could have changed over a migrate.
|
|
||||||
*/
|
|
||||||
if (mid == p2m_mid_missing) {
|
|
||||||
BUG_ON(mididx);
|
|
||||||
BUG_ON(mid_mfn_p != p2m_mid_missing_mfn);
|
|
||||||
p2m_top_mfn[topidx] = virt_to_mfn(p2m_mid_missing_mfn);
|
|
||||||
pfn += (P2M_MID_PER_PAGE - 1) * P2M_PER_PAGE;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mid_mfn_p == p2m_mid_missing_mfn) {
|
|
||||||
/*
|
|
||||||
* XXX boot-time only! We should never find
|
|
||||||
* missing parts of the mfn tree after
|
|
||||||
* runtime. extend_brk() will BUG if we call
|
|
||||||
* it too late.
|
|
||||||
*/
|
|
||||||
mid_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_mid_mfn_init(mid_mfn_p);
|
|
||||||
|
|
||||||
p2m_top_mfn_p[topidx] = mid_mfn_p;
|
|
||||||
}
|
|
||||||
|
|
||||||
p2m_top_mfn[topidx] = virt_to_mfn(mid_mfn_p);
|
|
||||||
mid_mfn_p[mididx] = virt_to_mfn(mid[mididx]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void xen_setup_mfn_list_list(void)
|
|
||||||
{
|
|
||||||
BUG_ON(HYPERVISOR_shared_info == &xen_dummy_shared_info);
|
|
||||||
|
|
||||||
HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
|
|
||||||
virt_to_mfn(p2m_top_mfn);
|
|
||||||
HYPERVISOR_shared_info->arch.max_pfn = xen_max_p2m_pfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set up p2m_top to point to the domain-builder provided p2m pages */
|
|
||||||
void __init xen_build_dynamic_phys_to_machine(void)
|
|
||||||
{
|
|
||||||
unsigned long *mfn_list = (unsigned long *)xen_start_info->mfn_list;
|
|
||||||
unsigned long max_pfn = min(MAX_DOMAIN_PAGES, xen_start_info->nr_pages);
|
|
||||||
unsigned long pfn;
|
|
||||||
|
|
||||||
xen_max_p2m_pfn = max_pfn;
|
|
||||||
|
|
||||||
p2m_missing = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_init(p2m_missing);
|
|
||||||
|
|
||||||
p2m_mid_missing = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_mid_init(p2m_mid_missing);
|
|
||||||
|
|
||||||
p2m_top = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_top_init(p2m_top);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The domain builder gives us a pre-constructed p2m array in
|
|
||||||
* mfn_list for all the pages initially given to us, so we just
|
|
||||||
* need to graft that into our tree structure.
|
|
||||||
*/
|
|
||||||
for (pfn = 0; pfn < max_pfn; pfn += P2M_PER_PAGE) {
|
|
||||||
unsigned topidx = p2m_top_index(pfn);
|
|
||||||
unsigned mididx = p2m_mid_index(pfn);
|
|
||||||
|
|
||||||
if (p2m_top[topidx] == p2m_mid_missing) {
|
|
||||||
unsigned long **mid = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
p2m_mid_init(mid);
|
|
||||||
|
|
||||||
p2m_top[topidx] = mid;
|
|
||||||
}
|
|
||||||
|
|
||||||
p2m_top[topidx][mididx] = &mfn_list[pfn];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long get_phys_to_machine(unsigned long pfn)
|
|
||||||
{
|
|
||||||
unsigned topidx, mididx, idx;
|
|
||||||
|
|
||||||
if (unlikely(pfn >= MAX_P2M_PFN))
|
|
||||||
return INVALID_P2M_ENTRY;
|
|
||||||
|
|
||||||
topidx = p2m_top_index(pfn);
|
|
||||||
mididx = p2m_mid_index(pfn);
|
|
||||||
idx = p2m_index(pfn);
|
|
||||||
|
|
||||||
return p2m_top[topidx][mididx][idx];
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(get_phys_to_machine);
|
|
||||||
|
|
||||||
static void *alloc_p2m_page(void)
|
|
||||||
{
|
|
||||||
return (void *)__get_free_page(GFP_KERNEL | __GFP_REPEAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_p2m_page(void *p)
|
|
||||||
{
|
|
||||||
free_page((unsigned long)p);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fully allocate the p2m structure for a given pfn. We need to check
|
|
||||||
* that both the top and mid levels are allocated, and make sure the
|
|
||||||
* parallel mfn tree is kept in sync. We may race with other cpus, so
|
|
||||||
* the new pages are installed with cmpxchg; if we lose the race then
|
|
||||||
* simply free the page we allocated and use the one that's there.
|
|
||||||
*/
|
|
||||||
static bool alloc_p2m(unsigned long pfn)
|
|
||||||
{
|
|
||||||
unsigned topidx, mididx;
|
|
||||||
unsigned long ***top_p, **mid;
|
|
||||||
unsigned long *top_mfn_p, *mid_mfn;
|
|
||||||
|
|
||||||
topidx = p2m_top_index(pfn);
|
|
||||||
mididx = p2m_mid_index(pfn);
|
|
||||||
|
|
||||||
top_p = &p2m_top[topidx];
|
|
||||||
mid = *top_p;
|
|
||||||
|
|
||||||
if (mid == p2m_mid_missing) {
|
|
||||||
/* Mid level is missing, allocate a new one */
|
|
||||||
mid = alloc_p2m_page();
|
|
||||||
if (!mid)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
p2m_mid_init(mid);
|
|
||||||
|
|
||||||
if (cmpxchg(top_p, p2m_mid_missing, mid) != p2m_mid_missing)
|
|
||||||
free_p2m_page(mid);
|
|
||||||
}
|
|
||||||
|
|
||||||
top_mfn_p = &p2m_top_mfn[topidx];
|
|
||||||
mid_mfn = p2m_top_mfn_p[topidx];
|
|
||||||
|
|
||||||
BUG_ON(virt_to_mfn(mid_mfn) != *top_mfn_p);
|
|
||||||
|
|
||||||
if (mid_mfn == p2m_mid_missing_mfn) {
|
|
||||||
/* Separately check the mid mfn level */
|
|
||||||
unsigned long missing_mfn;
|
|
||||||
unsigned long mid_mfn_mfn;
|
|
||||||
|
|
||||||
mid_mfn = alloc_p2m_page();
|
|
||||||
if (!mid_mfn)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
p2m_mid_mfn_init(mid_mfn);
|
|
||||||
|
|
||||||
missing_mfn = virt_to_mfn(p2m_mid_missing_mfn);
|
|
||||||
mid_mfn_mfn = virt_to_mfn(mid_mfn);
|
|
||||||
if (cmpxchg(top_mfn_p, missing_mfn, mid_mfn_mfn) != missing_mfn)
|
|
||||||
free_p2m_page(mid_mfn);
|
|
||||||
else
|
|
||||||
p2m_top_mfn_p[topidx] = mid_mfn;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (p2m_top[topidx][mididx] == p2m_missing) {
|
|
||||||
/* p2m leaf page is missing */
|
|
||||||
unsigned long *p2m;
|
|
||||||
|
|
||||||
p2m = alloc_p2m_page();
|
|
||||||
if (!p2m)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
p2m_init(p2m);
|
|
||||||
|
|
||||||
if (cmpxchg(&mid[mididx], p2m_missing, p2m) != p2m_missing)
|
|
||||||
free_p2m_page(p2m);
|
|
||||||
else
|
|
||||||
mid_mfn[mididx] = virt_to_mfn(p2m);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Try to install p2m mapping; fail if intermediate bits missing */
|
|
||||||
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
|
|
||||||
{
|
|
||||||
unsigned topidx, mididx, idx;
|
|
||||||
|
|
||||||
if (unlikely(pfn >= MAX_P2M_PFN)) {
|
|
||||||
BUG_ON(mfn != INVALID_P2M_ENTRY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
topidx = p2m_top_index(pfn);
|
|
||||||
mididx = p2m_mid_index(pfn);
|
|
||||||
idx = p2m_index(pfn);
|
|
||||||
|
|
||||||
if (p2m_top[topidx][mididx] == p2m_missing)
|
|
||||||
return mfn == INVALID_P2M_ENTRY;
|
|
||||||
|
|
||||||
p2m_top[topidx][mididx][idx] = mfn;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
|
|
||||||
{
|
|
||||||
if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) {
|
|
||||||
BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!__set_phys_to_machine(pfn, mfn))) {
|
|
||||||
if (!alloc_p2m(pfn))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!__set_phys_to_machine(pfn, mfn))
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned long arbitrary_virt_to_mfn(void *vaddr)
|
unsigned long arbitrary_virt_to_mfn(void *vaddr)
|
||||||
{
|
{
|
||||||
xmaddr_t maddr = arbitrary_virt_to_machine(vaddr);
|
xmaddr_t maddr = arbitrary_virt_to_machine(vaddr);
|
||||||
|
|
510
arch/x86/xen/p2m.c
Normal file
510
arch/x86/xen/p2m.c
Normal file
|
@ -0,0 +1,510 @@
|
||||||
|
/*
|
||||||
|
* Xen leaves the responsibility for maintaining p2m mappings to the
|
||||||
|
* guests themselves, but it must also access and update the p2m array
|
||||||
|
* during suspend/resume when all the pages are reallocated.
|
||||||
|
*
|
||||||
|
* The p2m table is logically a flat array, but we implement it as a
|
||||||
|
* three-level tree to allow the address space to be sparse.
|
||||||
|
*
|
||||||
|
* Xen
|
||||||
|
* |
|
||||||
|
* p2m_top p2m_top_mfn
|
||||||
|
* / \ / \
|
||||||
|
* p2m_mid p2m_mid p2m_mid_mfn p2m_mid_mfn
|
||||||
|
* / \ / \ / /
|
||||||
|
* p2m p2m p2m p2m p2m p2m p2m ...
|
||||||
|
*
|
||||||
|
* The p2m_mid_mfn pages are mapped by p2m_top_mfn_p.
|
||||||
|
*
|
||||||
|
* The p2m_top and p2m_top_mfn levels are limited to 1 page, so the
|
||||||
|
* maximum representable pseudo-physical address space is:
|
||||||
|
* P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE pages
|
||||||
|
*
|
||||||
|
* P2M_PER_PAGE depends on the architecture, as a mfn is always
|
||||||
|
* unsigned long (8 bytes on 64-bit, 4 bytes on 32), leading to
|
||||||
|
* 512 and 1024 entries respectively.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/hash.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
|
||||||
|
#include <asm/cache.h>
|
||||||
|
#include <asm/setup.h>
|
||||||
|
|
||||||
|
#include <asm/xen/page.h>
|
||||||
|
#include <asm/xen/hypercall.h>
|
||||||
|
#include <asm/xen/hypervisor.h>
|
||||||
|
|
||||||
|
#include "xen-ops.h"
|
||||||
|
|
||||||
|
static void __init m2p_override_init(void);
|
||||||
|
|
||||||
|
unsigned long xen_max_p2m_pfn __read_mostly;
|
||||||
|
|
||||||
|
#define P2M_PER_PAGE (PAGE_SIZE / sizeof(unsigned long))
|
||||||
|
#define P2M_MID_PER_PAGE (PAGE_SIZE / sizeof(unsigned long *))
|
||||||
|
#define P2M_TOP_PER_PAGE (PAGE_SIZE / sizeof(unsigned long **))
|
||||||
|
|
||||||
|
#define MAX_P2M_PFN (P2M_TOP_PER_PAGE * P2M_MID_PER_PAGE * P2M_PER_PAGE)
|
||||||
|
|
||||||
|
/* Placeholders for holes in the address space */
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long, p2m_missing, P2M_PER_PAGE);
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long *, p2m_mid_missing, P2M_MID_PER_PAGE);
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long, p2m_mid_missing_mfn, P2M_MID_PER_PAGE);
|
||||||
|
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long **, p2m_top, P2M_TOP_PER_PAGE);
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long, p2m_top_mfn, P2M_TOP_PER_PAGE);
|
||||||
|
static RESERVE_BRK_ARRAY(unsigned long *, p2m_top_mfn_p, P2M_TOP_PER_PAGE);
|
||||||
|
|
||||||
|
RESERVE_BRK(p2m_mid, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
|
||||||
|
RESERVE_BRK(p2m_mid_mfn, PAGE_SIZE * (MAX_DOMAIN_PAGES / (P2M_PER_PAGE * P2M_MID_PER_PAGE)));
|
||||||
|
|
||||||
|
static inline unsigned p2m_top_index(unsigned long pfn)
|
||||||
|
{
|
||||||
|
BUG_ON(pfn >= MAX_P2M_PFN);
|
||||||
|
return pfn / (P2M_MID_PER_PAGE * P2M_PER_PAGE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned p2m_mid_index(unsigned long pfn)
|
||||||
|
{
|
||||||
|
return (pfn / P2M_PER_PAGE) % P2M_MID_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned p2m_index(unsigned long pfn)
|
||||||
|
{
|
||||||
|
return pfn % P2M_PER_PAGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_top_init(unsigned long ***top)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
||||||
|
top[i] = p2m_mid_missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_top_mfn_init(unsigned long *top)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
||||||
|
top[i] = virt_to_mfn(p2m_mid_missing_mfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_top_mfn_p_init(unsigned long **top)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_TOP_PER_PAGE; i++)
|
||||||
|
top[i] = p2m_mid_missing_mfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_mid_init(unsigned long **mid)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
||||||
|
mid[i] = p2m_missing;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_mid_mfn_init(unsigned long *mid)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
||||||
|
mid[i] = virt_to_mfn(p2m_missing);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void p2m_init(unsigned long *p2m)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < P2M_MID_PER_PAGE; i++)
|
||||||
|
p2m[i] = INVALID_P2M_ENTRY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Build the parallel p2m_top_mfn and p2m_mid_mfn structures
|
||||||
|
*
|
||||||
|
* This is called both at boot time, and after resuming from suspend:
|
||||||
|
* - At boot time we're called very early, and must use extend_brk()
|
||||||
|
* to allocate memory.
|
||||||
|
*
|
||||||
|
* - After resume we're called from within stop_machine, but the mfn
|
||||||
|
* tree should alreay be completely allocated.
|
||||||
|
*/
|
||||||
|
void xen_build_mfn_list_list(void)
|
||||||
|
{
|
||||||
|
unsigned long pfn;
|
||||||
|
|
||||||
|
/* Pre-initialize p2m_top_mfn to be completely missing */
|
||||||
|
if (p2m_top_mfn == NULL) {
|
||||||
|
p2m_mid_missing_mfn = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_mid_mfn_init(p2m_mid_missing_mfn);
|
||||||
|
|
||||||
|
p2m_top_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_top_mfn_p_init(p2m_top_mfn_p);
|
||||||
|
|
||||||
|
p2m_top_mfn = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_top_mfn_init(p2m_top_mfn);
|
||||||
|
} else {
|
||||||
|
/* Reinitialise, mfn's all change after migration */
|
||||||
|
p2m_mid_mfn_init(p2m_mid_missing_mfn);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (pfn = 0; pfn < xen_max_p2m_pfn; pfn += P2M_PER_PAGE) {
|
||||||
|
unsigned topidx = p2m_top_index(pfn);
|
||||||
|
unsigned mididx = p2m_mid_index(pfn);
|
||||||
|
unsigned long **mid;
|
||||||
|
unsigned long *mid_mfn_p;
|
||||||
|
|
||||||
|
mid = p2m_top[topidx];
|
||||||
|
mid_mfn_p = p2m_top_mfn_p[topidx];
|
||||||
|
|
||||||
|
/* Don't bother allocating any mfn mid levels if
|
||||||
|
* they're just missing, just update the stored mfn,
|
||||||
|
* since all could have changed over a migrate.
|
||||||
|
*/
|
||||||
|
if (mid == p2m_mid_missing) {
|
||||||
|
BUG_ON(mididx);
|
||||||
|
BUG_ON(mid_mfn_p != p2m_mid_missing_mfn);
|
||||||
|
p2m_top_mfn[topidx] = virt_to_mfn(p2m_mid_missing_mfn);
|
||||||
|
pfn += (P2M_MID_PER_PAGE - 1) * P2M_PER_PAGE;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mid_mfn_p == p2m_mid_missing_mfn) {
|
||||||
|
/*
|
||||||
|
* XXX boot-time only! We should never find
|
||||||
|
* missing parts of the mfn tree after
|
||||||
|
* runtime. extend_brk() will BUG if we call
|
||||||
|
* it too late.
|
||||||
|
*/
|
||||||
|
mid_mfn_p = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_mid_mfn_init(mid_mfn_p);
|
||||||
|
|
||||||
|
p2m_top_mfn_p[topidx] = mid_mfn_p;
|
||||||
|
}
|
||||||
|
|
||||||
|
p2m_top_mfn[topidx] = virt_to_mfn(mid_mfn_p);
|
||||||
|
mid_mfn_p[mididx] = virt_to_mfn(mid[mididx]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void xen_setup_mfn_list_list(void)
|
||||||
|
{
|
||||||
|
BUG_ON(HYPERVISOR_shared_info == &xen_dummy_shared_info);
|
||||||
|
|
||||||
|
HYPERVISOR_shared_info->arch.pfn_to_mfn_frame_list_list =
|
||||||
|
virt_to_mfn(p2m_top_mfn);
|
||||||
|
HYPERVISOR_shared_info->arch.max_pfn = xen_max_p2m_pfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up p2m_top to point to the domain-builder provided p2m pages */
|
||||||
|
void __init xen_build_dynamic_phys_to_machine(void)
|
||||||
|
{
|
||||||
|
unsigned long *mfn_list = (unsigned long *)xen_start_info->mfn_list;
|
||||||
|
unsigned long max_pfn = min(MAX_DOMAIN_PAGES, xen_start_info->nr_pages);
|
||||||
|
unsigned long pfn;
|
||||||
|
|
||||||
|
xen_max_p2m_pfn = max_pfn;
|
||||||
|
|
||||||
|
p2m_missing = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_init(p2m_missing);
|
||||||
|
|
||||||
|
p2m_mid_missing = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_mid_init(p2m_mid_missing);
|
||||||
|
|
||||||
|
p2m_top = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_top_init(p2m_top);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The domain builder gives us a pre-constructed p2m array in
|
||||||
|
* mfn_list for all the pages initially given to us, so we just
|
||||||
|
* need to graft that into our tree structure.
|
||||||
|
*/
|
||||||
|
for (pfn = 0; pfn < max_pfn; pfn += P2M_PER_PAGE) {
|
||||||
|
unsigned topidx = p2m_top_index(pfn);
|
||||||
|
unsigned mididx = p2m_mid_index(pfn);
|
||||||
|
|
||||||
|
if (p2m_top[topidx] == p2m_mid_missing) {
|
||||||
|
unsigned long **mid = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
p2m_mid_init(mid);
|
||||||
|
|
||||||
|
p2m_top[topidx] = mid;
|
||||||
|
}
|
||||||
|
|
||||||
|
p2m_top[topidx][mididx] = &mfn_list[pfn];
|
||||||
|
}
|
||||||
|
|
||||||
|
m2p_override_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long get_phys_to_machine(unsigned long pfn)
|
||||||
|
{
|
||||||
|
unsigned topidx, mididx, idx;
|
||||||
|
|
||||||
|
if (unlikely(pfn >= MAX_P2M_PFN))
|
||||||
|
return INVALID_P2M_ENTRY;
|
||||||
|
|
||||||
|
topidx = p2m_top_index(pfn);
|
||||||
|
mididx = p2m_mid_index(pfn);
|
||||||
|
idx = p2m_index(pfn);
|
||||||
|
|
||||||
|
return p2m_top[topidx][mididx][idx];
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(get_phys_to_machine);
|
||||||
|
|
||||||
|
static void *alloc_p2m_page(void)
|
||||||
|
{
|
||||||
|
return (void *)__get_free_page(GFP_KERNEL | __GFP_REPEAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_p2m_page(void *p)
|
||||||
|
{
|
||||||
|
free_page((unsigned long)p);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fully allocate the p2m structure for a given pfn. We need to check
|
||||||
|
* that both the top and mid levels are allocated, and make sure the
|
||||||
|
* parallel mfn tree is kept in sync. We may race with other cpus, so
|
||||||
|
* the new pages are installed with cmpxchg; if we lose the race then
|
||||||
|
* simply free the page we allocated and use the one that's there.
|
||||||
|
*/
|
||||||
|
static bool alloc_p2m(unsigned long pfn)
|
||||||
|
{
|
||||||
|
unsigned topidx, mididx;
|
||||||
|
unsigned long ***top_p, **mid;
|
||||||
|
unsigned long *top_mfn_p, *mid_mfn;
|
||||||
|
|
||||||
|
topidx = p2m_top_index(pfn);
|
||||||
|
mididx = p2m_mid_index(pfn);
|
||||||
|
|
||||||
|
top_p = &p2m_top[topidx];
|
||||||
|
mid = *top_p;
|
||||||
|
|
||||||
|
if (mid == p2m_mid_missing) {
|
||||||
|
/* Mid level is missing, allocate a new one */
|
||||||
|
mid = alloc_p2m_page();
|
||||||
|
if (!mid)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
p2m_mid_init(mid);
|
||||||
|
|
||||||
|
if (cmpxchg(top_p, p2m_mid_missing, mid) != p2m_mid_missing)
|
||||||
|
free_p2m_page(mid);
|
||||||
|
}
|
||||||
|
|
||||||
|
top_mfn_p = &p2m_top_mfn[topidx];
|
||||||
|
mid_mfn = p2m_top_mfn_p[topidx];
|
||||||
|
|
||||||
|
BUG_ON(virt_to_mfn(mid_mfn) != *top_mfn_p);
|
||||||
|
|
||||||
|
if (mid_mfn == p2m_mid_missing_mfn) {
|
||||||
|
/* Separately check the mid mfn level */
|
||||||
|
unsigned long missing_mfn;
|
||||||
|
unsigned long mid_mfn_mfn;
|
||||||
|
|
||||||
|
mid_mfn = alloc_p2m_page();
|
||||||
|
if (!mid_mfn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
p2m_mid_mfn_init(mid_mfn);
|
||||||
|
|
||||||
|
missing_mfn = virt_to_mfn(p2m_mid_missing_mfn);
|
||||||
|
mid_mfn_mfn = virt_to_mfn(mid_mfn);
|
||||||
|
if (cmpxchg(top_mfn_p, missing_mfn, mid_mfn_mfn) != missing_mfn)
|
||||||
|
free_p2m_page(mid_mfn);
|
||||||
|
else
|
||||||
|
p2m_top_mfn_p[topidx] = mid_mfn;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p2m_top[topidx][mididx] == p2m_missing) {
|
||||||
|
/* p2m leaf page is missing */
|
||||||
|
unsigned long *p2m;
|
||||||
|
|
||||||
|
p2m = alloc_p2m_page();
|
||||||
|
if (!p2m)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
p2m_init(p2m);
|
||||||
|
|
||||||
|
if (cmpxchg(&mid[mididx], p2m_missing, p2m) != p2m_missing)
|
||||||
|
free_p2m_page(p2m);
|
||||||
|
else
|
||||||
|
mid_mfn[mididx] = virt_to_mfn(p2m);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to install p2m mapping; fail if intermediate bits missing */
|
||||||
|
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
|
||||||
|
{
|
||||||
|
unsigned topidx, mididx, idx;
|
||||||
|
|
||||||
|
if (unlikely(pfn >= MAX_P2M_PFN)) {
|
||||||
|
BUG_ON(mfn != INVALID_P2M_ENTRY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
topidx = p2m_top_index(pfn);
|
||||||
|
mididx = p2m_mid_index(pfn);
|
||||||
|
idx = p2m_index(pfn);
|
||||||
|
|
||||||
|
if (p2m_top[topidx][mididx] == p2m_missing)
|
||||||
|
return mfn == INVALID_P2M_ENTRY;
|
||||||
|
|
||||||
|
p2m_top[topidx][mididx][idx] = mfn;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_phys_to_machine(unsigned long pfn, unsigned long mfn)
|
||||||
|
{
|
||||||
|
if (unlikely(xen_feature(XENFEAT_auto_translated_physmap))) {
|
||||||
|
BUG_ON(pfn != mfn && mfn != INVALID_P2M_ENTRY);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!__set_phys_to_machine(pfn, mfn))) {
|
||||||
|
if (!alloc_p2m(pfn))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!__set_phys_to_machine(pfn, mfn))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define M2P_OVERRIDE_HASH_SHIFT 10
|
||||||
|
#define M2P_OVERRIDE_HASH (1 << M2P_OVERRIDE_HASH_SHIFT)
|
||||||
|
|
||||||
|
static RESERVE_BRK_ARRAY(struct list_head, m2p_overrides, M2P_OVERRIDE_HASH);
|
||||||
|
static DEFINE_SPINLOCK(m2p_override_lock);
|
||||||
|
|
||||||
|
static void __init m2p_override_init(void)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
m2p_overrides = extend_brk(sizeof(*m2p_overrides) * M2P_OVERRIDE_HASH,
|
||||||
|
sizeof(unsigned long));
|
||||||
|
|
||||||
|
for (i = 0; i < M2P_OVERRIDE_HASH; i++)
|
||||||
|
INIT_LIST_HEAD(&m2p_overrides[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned long mfn_hash(unsigned long mfn)
|
||||||
|
{
|
||||||
|
return hash_long(mfn, M2P_OVERRIDE_HASH_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add an MFN override for a particular page */
|
||||||
|
int m2p_add_override(unsigned long mfn, struct page *page)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned long pfn;
|
||||||
|
unsigned long address;
|
||||||
|
unsigned level;
|
||||||
|
pte_t *ptep = NULL;
|
||||||
|
|
||||||
|
pfn = page_to_pfn(page);
|
||||||
|
if (!PageHighMem(page)) {
|
||||||
|
address = (unsigned long)__va(pfn << PAGE_SHIFT);
|
||||||
|
ptep = lookup_address(address, &level);
|
||||||
|
|
||||||
|
if (WARN(ptep == NULL || level != PG_LEVEL_4K,
|
||||||
|
"m2p_add_override: pfn %lx not mapped", pfn))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
page->private = mfn;
|
||||||
|
page->index = pfn_to_mfn(pfn);
|
||||||
|
|
||||||
|
__set_phys_to_machine(pfn, FOREIGN_FRAME(mfn));
|
||||||
|
if (!PageHighMem(page))
|
||||||
|
/* Just zap old mapping for now */
|
||||||
|
pte_clear(&init_mm, address, ptep);
|
||||||
|
|
||||||
|
spin_lock_irqsave(&m2p_override_lock, flags);
|
||||||
|
list_add(&page->lru, &m2p_overrides[mfn_hash(mfn)]);
|
||||||
|
spin_unlock_irqrestore(&m2p_override_lock, flags);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int m2p_remove_override(struct page *page)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
unsigned long mfn;
|
||||||
|
unsigned long pfn;
|
||||||
|
unsigned long address;
|
||||||
|
unsigned level;
|
||||||
|
pte_t *ptep = NULL;
|
||||||
|
|
||||||
|
pfn = page_to_pfn(page);
|
||||||
|
mfn = get_phys_to_machine(pfn);
|
||||||
|
if (mfn == INVALID_P2M_ENTRY || !(mfn & FOREIGN_FRAME_BIT))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (!PageHighMem(page)) {
|
||||||
|
address = (unsigned long)__va(pfn << PAGE_SHIFT);
|
||||||
|
ptep = lookup_address(address, &level);
|
||||||
|
|
||||||
|
if (WARN(ptep == NULL || level != PG_LEVEL_4K,
|
||||||
|
"m2p_remove_override: pfn %lx not mapped", pfn))
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock_irqsave(&m2p_override_lock, flags);
|
||||||
|
list_del(&page->lru);
|
||||||
|
spin_unlock_irqrestore(&m2p_override_lock, flags);
|
||||||
|
__set_phys_to_machine(pfn, page->index);
|
||||||
|
|
||||||
|
if (!PageHighMem(page))
|
||||||
|
set_pte_at(&init_mm, address, ptep,
|
||||||
|
pfn_pte(pfn, PAGE_KERNEL));
|
||||||
|
/* No tlb flush necessary because the caller already
|
||||||
|
* left the pte unmapped. */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct page *m2p_find_override(unsigned long mfn)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
struct list_head *bucket = &m2p_overrides[mfn_hash(mfn)];
|
||||||
|
struct page *p, *ret;
|
||||||
|
|
||||||
|
ret = NULL;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&m2p_override_lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(p, bucket, lru) {
|
||||||
|
if (p->private == mfn) {
|
||||||
|
ret = p;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&m2p_override_lock, flags);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long m2p_find_override_pfn(unsigned long mfn, unsigned long pfn)
|
||||||
|
{
|
||||||
|
struct page *p = m2p_find_override(mfn);
|
||||||
|
unsigned long ret = pfn;
|
||||||
|
|
||||||
|
if (p)
|
||||||
|
ret = page_to_pfn(p);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(m2p_find_override_pfn);
|
|
@ -83,6 +83,9 @@
|
||||||
#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
|
#define MADV_MERGEABLE 12 /* KSM may merge identical pages */
|
||||||
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
#define MADV_UNMERGEABLE 13 /* KSM may not merge identical pages */
|
||||||
|
|
||||||
|
#define MADV_HUGEPAGE 14 /* Worth backing with hugepages */
|
||||||
|
#define MADV_NOHUGEPAGE 15 /* Not worth backing with hugepages */
|
||||||
|
|
||||||
/* compatibility flags */
|
/* compatibility flags */
|
||||||
#define MAP_FILE 0
|
#define MAP_FILE 0
|
||||||
|
|
||||||
|
|
|
@ -117,12 +117,21 @@ static ssize_t node_read_meminfo(struct sys_device * dev,
|
||||||
"Node %d WritebackTmp: %8lu kB\n"
|
"Node %d WritebackTmp: %8lu kB\n"
|
||||||
"Node %d Slab: %8lu kB\n"
|
"Node %d Slab: %8lu kB\n"
|
||||||
"Node %d SReclaimable: %8lu kB\n"
|
"Node %d SReclaimable: %8lu kB\n"
|
||||||
"Node %d SUnreclaim: %8lu kB\n",
|
"Node %d SUnreclaim: %8lu kB\n"
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
"Node %d AnonHugePages: %8lu kB\n"
|
||||||
|
#endif
|
||||||
|
,
|
||||||
nid, K(node_page_state(nid, NR_FILE_DIRTY)),
|
nid, K(node_page_state(nid, NR_FILE_DIRTY)),
|
||||||
nid, K(node_page_state(nid, NR_WRITEBACK)),
|
nid, K(node_page_state(nid, NR_WRITEBACK)),
|
||||||
nid, K(node_page_state(nid, NR_FILE_PAGES)),
|
nid, K(node_page_state(nid, NR_FILE_PAGES)),
|
||||||
nid, K(node_page_state(nid, NR_FILE_MAPPED)),
|
nid, K(node_page_state(nid, NR_FILE_MAPPED)),
|
||||||
nid, K(node_page_state(nid, NR_ANON_PAGES)),
|
nid, K(node_page_state(nid, NR_ANON_PAGES)
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
+ node_page_state(nid, NR_ANON_TRANSPARENT_HUGEPAGES) *
|
||||||
|
HPAGE_PMD_NR
|
||||||
|
#endif
|
||||||
|
),
|
||||||
nid, K(node_page_state(nid, NR_SHMEM)),
|
nid, K(node_page_state(nid, NR_SHMEM)),
|
||||||
nid, node_page_state(nid, NR_KERNEL_STACK) *
|
nid, node_page_state(nid, NR_KERNEL_STACK) *
|
||||||
THREAD_SIZE / 1024,
|
THREAD_SIZE / 1024,
|
||||||
|
@ -133,7 +142,13 @@ static ssize_t node_read_meminfo(struct sys_device * dev,
|
||||||
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
|
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
|
||||||
node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
|
node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
|
||||||
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
|
nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
|
||||||
nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE)));
|
nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE))
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
, nid,
|
||||||
|
K(node_page_state(nid, NR_ANON_TRANSPARENT_HUGEPAGES) *
|
||||||
|
HPAGE_PMD_NR)
|
||||||
|
#endif
|
||||||
|
);
|
||||||
n += hugetlb_report_node_meminfo(nid, buf + n);
|
n += hugetlb_report_node_meminfo(nid, buf + n);
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
|
@ -240,6 +240,30 @@ config DM_MIRROR
|
||||||
Allow volume managers to mirror logical volumes, also
|
Allow volume managers to mirror logical volumes, also
|
||||||
needed for live data migration tools such as 'pvmove'.
|
needed for live data migration tools such as 'pvmove'.
|
||||||
|
|
||||||
|
config DM_RAID
|
||||||
|
tristate "RAID 4/5/6 target (EXPERIMENTAL)"
|
||||||
|
depends on BLK_DEV_DM && EXPERIMENTAL
|
||||||
|
select MD_RAID456
|
||||||
|
select BLK_DEV_MD
|
||||||
|
---help---
|
||||||
|
A dm target that supports 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
|
||||||
|
of a single drive. For a given sector (row) number, (N - 1) drives
|
||||||
|
contain data sectors, and one drive contains the parity protection.
|
||||||
|
For a RAID-4 set, the parity blocks are present on a single drive,
|
||||||
|
while a RAID-5 set distributes the parity across the drives in one
|
||||||
|
of the available parity distribution methods.
|
||||||
|
|
||||||
|
A RAID-6 set of N drives with a capacity of C MB per drive
|
||||||
|
provides the capacity of C * (N - 2) MB, and protects
|
||||||
|
against a failure of any two drives. For a given sector
|
||||||
|
(row) number, (N - 2) drives contain data sectors, and two
|
||||||
|
drives contains two independent redundancy syndromes. Like
|
||||||
|
RAID-5, RAID-6 distributes the syndromes across the drives
|
||||||
|
in one of the available parity distribution methods.
|
||||||
|
|
||||||
config DM_LOG_USERSPACE
|
config DM_LOG_USERSPACE
|
||||||
tristate "Mirror userspace logging (EXPERIMENTAL)"
|
tristate "Mirror userspace logging (EXPERIMENTAL)"
|
||||||
depends on DM_MIRROR && EXPERIMENTAL && NET
|
depends on DM_MIRROR && EXPERIMENTAL && NET
|
||||||
|
|
|
@ -36,6 +36,7 @@ obj-$(CONFIG_DM_SNAPSHOT) += dm-snapshot.o
|
||||||
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
|
obj-$(CONFIG_DM_MIRROR) += dm-mirror.o dm-log.o dm-region-hash.o
|
||||||
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
|
obj-$(CONFIG_DM_LOG_USERSPACE) += dm-log-userspace.o
|
||||||
obj-$(CONFIG_DM_ZERO) += dm-zero.o
|
obj-$(CONFIG_DM_ZERO) += dm-zero.o
|
||||||
|
obj-$(CONFIG_DM_RAID) += dm-raid.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_DM_UEVENT),y)
|
ifeq ($(CONFIG_DM_UEVENT),y)
|
||||||
dm-mod-objs += dm-uevent.o
|
dm-mod-objs += dm-uevent.o
|
||||||
|
|
|
@ -210,11 +210,11 @@ static struct page *read_sb_page(mddev_t *mddev, loff_t offset,
|
||||||
|| test_bit(Faulty, &rdev->flags))
|
|| test_bit(Faulty, &rdev->flags))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
target = rdev->sb_start + offset + index * (PAGE_SIZE/512);
|
target = offset + index * (PAGE_SIZE/512);
|
||||||
|
|
||||||
if (sync_page_io(rdev, target,
|
if (sync_page_io(rdev, target,
|
||||||
roundup(size, bdev_logical_block_size(rdev->bdev)),
|
roundup(size, bdev_logical_block_size(rdev->bdev)),
|
||||||
page, READ)) {
|
page, READ, true)) {
|
||||||
page->index = index;
|
page->index = index;
|
||||||
attach_page_buffers(page, NULL); /* so that free_buffer will
|
attach_page_buffers(page, NULL); /* so that free_buffer will
|
||||||
* quietly no-op */
|
* quietly no-op */
|
||||||
|
@ -264,14 +264,18 @@ static mdk_rdev_t *next_active_rdev(mdk_rdev_t *rdev, mddev_t *mddev)
|
||||||
static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
|
static int write_sb_page(struct bitmap *bitmap, struct page *page, int wait)
|
||||||
{
|
{
|
||||||
mdk_rdev_t *rdev = NULL;
|
mdk_rdev_t *rdev = NULL;
|
||||||
|
struct block_device *bdev;
|
||||||
mddev_t *mddev = bitmap->mddev;
|
mddev_t *mddev = bitmap->mddev;
|
||||||
|
|
||||||
while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
|
while ((rdev = next_active_rdev(rdev, mddev)) != NULL) {
|
||||||
int size = PAGE_SIZE;
|
int size = PAGE_SIZE;
|
||||||
loff_t offset = mddev->bitmap_info.offset;
|
loff_t offset = mddev->bitmap_info.offset;
|
||||||
|
|
||||||
|
bdev = (rdev->meta_bdev) ? rdev->meta_bdev : rdev->bdev;
|
||||||
|
|
||||||
if (page->index == bitmap->file_pages-1)
|
if (page->index == bitmap->file_pages-1)
|
||||||
size = roundup(bitmap->last_page_size,
|
size = roundup(bitmap->last_page_size,
|
||||||
bdev_logical_block_size(rdev->bdev));
|
bdev_logical_block_size(bdev));
|
||||||
/* Just make sure we aren't corrupting data or
|
/* Just make sure we aren't corrupting data or
|
||||||
* metadata
|
* metadata
|
||||||
*/
|
*/
|
||||||
|
@ -1542,7 +1546,7 @@ void bitmap_cond_end_sync(struct bitmap *bitmap, sector_t sector)
|
||||||
wait_event(bitmap->mddev->recovery_wait,
|
wait_event(bitmap->mddev->recovery_wait,
|
||||||
atomic_read(&bitmap->mddev->recovery_active) == 0);
|
atomic_read(&bitmap->mddev->recovery_active) == 0);
|
||||||
|
|
||||||
bitmap->mddev->curr_resync_completed = bitmap->mddev->curr_resync;
|
bitmap->mddev->curr_resync_completed = sector;
|
||||||
set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
|
set_bit(MD_CHANGE_CLEAN, &bitmap->mddev->flags);
|
||||||
sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1);
|
sector &= ~((1ULL << CHUNK_BLOCK_SHIFT(bitmap)) - 1);
|
||||||
s = 0;
|
s = 0;
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -352,7 +352,7 @@ static int __init dm_delay_init(void)
|
||||||
{
|
{
|
||||||
int r = -ENOMEM;
|
int r = -ENOMEM;
|
||||||
|
|
||||||
kdelayd_wq = create_workqueue("kdelayd");
|
kdelayd_wq = alloc_workqueue("kdelayd", WQ_MEM_RECLAIM, 0);
|
||||||
if (!kdelayd_wq) {
|
if (!kdelayd_wq) {
|
||||||
DMERR("Couldn't start kdelayd");
|
DMERR("Couldn't start kdelayd");
|
||||||
goto bad_queue;
|
goto bad_queue;
|
||||||
|
|
|
@ -295,19 +295,55 @@ retry:
|
||||||
DMWARN("remove_all left %d open device(s)", dev_skipped);
|
DMWARN("remove_all left %d open device(s)", dev_skipped);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set the uuid of a hash_cell that isn't already set.
|
||||||
|
*/
|
||||||
|
static void __set_cell_uuid(struct hash_cell *hc, char *new_uuid)
|
||||||
|
{
|
||||||
|
mutex_lock(&dm_hash_cells_mutex);
|
||||||
|
hc->uuid = new_uuid;
|
||||||
|
mutex_unlock(&dm_hash_cells_mutex);
|
||||||
|
|
||||||
|
list_add(&hc->uuid_list, _uuid_buckets + hash_str(new_uuid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Changes the name of a hash_cell and returns the old name for
|
||||||
|
* the caller to free.
|
||||||
|
*/
|
||||||
|
static char *__change_cell_name(struct hash_cell *hc, char *new_name)
|
||||||
|
{
|
||||||
|
char *old_name;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Rename and move the name cell.
|
||||||
|
*/
|
||||||
|
list_del(&hc->name_list);
|
||||||
|
old_name = hc->name;
|
||||||
|
|
||||||
|
mutex_lock(&dm_hash_cells_mutex);
|
||||||
|
hc->name = new_name;
|
||||||
|
mutex_unlock(&dm_hash_cells_mutex);
|
||||||
|
|
||||||
|
list_add(&hc->name_list, _name_buckets + hash_str(new_name));
|
||||||
|
|
||||||
|
return old_name;
|
||||||
|
}
|
||||||
|
|
||||||
static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||||
const char *new)
|
const char *new)
|
||||||
{
|
{
|
||||||
char *new_name, *old_name;
|
char *new_data, *old_name = NULL;
|
||||||
struct hash_cell *hc;
|
struct hash_cell *hc;
|
||||||
struct dm_table *table;
|
struct dm_table *table;
|
||||||
struct mapped_device *md;
|
struct mapped_device *md;
|
||||||
|
unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* duplicate new.
|
* duplicate new.
|
||||||
*/
|
*/
|
||||||
new_name = kstrdup(new, GFP_KERNEL);
|
new_data = kstrdup(new, GFP_KERNEL);
|
||||||
if (!new_name)
|
if (!new_data)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
down_write(&_hash_lock);
|
down_write(&_hash_lock);
|
||||||
|
@ -315,13 +351,19 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||||
/*
|
/*
|
||||||
* Is new free ?
|
* Is new free ?
|
||||||
*/
|
*/
|
||||||
hc = __get_name_cell(new);
|
if (change_uuid)
|
||||||
|
hc = __get_uuid_cell(new);
|
||||||
|
else
|
||||||
|
hc = __get_name_cell(new);
|
||||||
|
|
||||||
if (hc) {
|
if (hc) {
|
||||||
DMWARN("asked to rename to an already-existing name %s -> %s",
|
DMWARN("Unable to change %s on mapped device %s to one that "
|
||||||
|
"already exists: %s",
|
||||||
|
change_uuid ? "uuid" : "name",
|
||||||
param->name, new);
|
param->name, new);
|
||||||
dm_put(hc->md);
|
dm_put(hc->md);
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
kfree(new_name);
|
kfree(new_data);
|
||||||
return ERR_PTR(-EBUSY);
|
return ERR_PTR(-EBUSY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -330,22 +372,30 @@ static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,
|
||||||
*/
|
*/
|
||||||
hc = __get_name_cell(param->name);
|
hc = __get_name_cell(param->name);
|
||||||
if (!hc) {
|
if (!hc) {
|
||||||
DMWARN("asked to rename a non-existent device %s -> %s",
|
DMWARN("Unable to rename non-existent device, %s to %s%s",
|
||||||
param->name, new);
|
param->name, change_uuid ? "uuid " : "", new);
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
kfree(new_name);
|
kfree(new_data);
|
||||||
return ERR_PTR(-ENXIO);
|
return ERR_PTR(-ENXIO);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* rename and move the name cell.
|
* Does this device already have a uuid?
|
||||||
*/
|
*/
|
||||||
list_del(&hc->name_list);
|
if (change_uuid && hc->uuid) {
|
||||||
old_name = hc->name;
|
DMWARN("Unable to change uuid of mapped device %s to %s "
|
||||||
mutex_lock(&dm_hash_cells_mutex);
|
"because uuid is already set to %s",
|
||||||
hc->name = new_name;
|
param->name, new, hc->uuid);
|
||||||
mutex_unlock(&dm_hash_cells_mutex);
|
dm_put(hc->md);
|
||||||
list_add(&hc->name_list, _name_buckets + hash_str(new_name));
|
up_write(&_hash_lock);
|
||||||
|
kfree(new_data);
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change_uuid)
|
||||||
|
__set_cell_uuid(hc, new_data);
|
||||||
|
else
|
||||||
|
old_name = __change_cell_name(hc, new_data);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Wake up any dm event waiters.
|
* Wake up any dm event waiters.
|
||||||
|
@ -729,7 +779,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
|
||||||
hc = __find_device_hash_cell(param);
|
hc = __find_device_hash_cell(param);
|
||||||
|
|
||||||
if (!hc) {
|
if (!hc) {
|
||||||
DMWARN("device doesn't appear to be in the dev hash table.");
|
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -741,7 +791,7 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
|
||||||
*/
|
*/
|
||||||
r = dm_lock_for_deletion(md);
|
r = dm_lock_for_deletion(md);
|
||||||
if (r) {
|
if (r) {
|
||||||
DMWARN("unable to remove open device %s", hc->name);
|
DMDEBUG_LIMIT("unable to remove open device %s", hc->name);
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
dm_put(md);
|
dm_put(md);
|
||||||
return r;
|
return r;
|
||||||
|
@ -774,21 +824,24 @@ static int invalid_str(char *str, void *end)
|
||||||
static int dev_rename(struct dm_ioctl *param, size_t param_size)
|
static int dev_rename(struct dm_ioctl *param, size_t param_size)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
char *new_name = (char *) param + param->data_start;
|
char *new_data = (char *) param + param->data_start;
|
||||||
struct mapped_device *md;
|
struct mapped_device *md;
|
||||||
|
unsigned change_uuid = (param->flags & DM_UUID_FLAG) ? 1 : 0;
|
||||||
|
|
||||||
if (new_name < param->data ||
|
if (new_data < param->data ||
|
||||||
invalid_str(new_name, (void *) param + param_size) ||
|
invalid_str(new_data, (void *) param + param_size) ||
|
||||||
strlen(new_name) > DM_NAME_LEN - 1) {
|
strlen(new_data) > (change_uuid ? DM_UUID_LEN - 1 : DM_NAME_LEN - 1)) {
|
||||||
DMWARN("Invalid new logical volume name supplied.");
|
DMWARN("Invalid new mapped device name or uuid string supplied.");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = check_name(new_name);
|
if (!change_uuid) {
|
||||||
if (r)
|
r = check_name(new_data);
|
||||||
return r;
|
if (r)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
md = dm_hash_rename(param, new_name);
|
md = dm_hash_rename(param, new_data);
|
||||||
if (IS_ERR(md))
|
if (IS_ERR(md))
|
||||||
return PTR_ERR(md);
|
return PTR_ERR(md);
|
||||||
|
|
||||||
|
@ -885,7 +938,7 @@ static int do_resume(struct dm_ioctl *param)
|
||||||
|
|
||||||
hc = __find_device_hash_cell(param);
|
hc = __find_device_hash_cell(param);
|
||||||
if (!hc) {
|
if (!hc) {
|
||||||
DMWARN("device doesn't appear to be in the dev hash table.");
|
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -1212,7 +1265,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
|
||||||
|
|
||||||
hc = __find_device_hash_cell(param);
|
hc = __find_device_hash_cell(param);
|
||||||
if (!hc) {
|
if (!hc) {
|
||||||
DMWARN("device doesn't appear to be in the dev hash table.");
|
DMDEBUG_LIMIT("device doesn't appear to be in the dev hash table.");
|
||||||
up_write(&_hash_lock);
|
up_write(&_hash_lock);
|
||||||
return -ENXIO;
|
return -ENXIO;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,13 @@ struct dm_kcopyd_client {
|
||||||
unsigned int nr_pages;
|
unsigned int nr_pages;
|
||||||
unsigned int nr_free_pages;
|
unsigned int nr_free_pages;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Block devices to unplug.
|
||||||
|
* Non-NULL pointer means that a block device has some pending requests
|
||||||
|
* and needs to be unplugged.
|
||||||
|
*/
|
||||||
|
struct block_device *unplug[2];
|
||||||
|
|
||||||
struct dm_io_client *io_client;
|
struct dm_io_client *io_client;
|
||||||
|
|
||||||
wait_queue_head_t destroyq;
|
wait_queue_head_t destroyq;
|
||||||
|
@ -308,6 +315,31 @@ static int run_complete_job(struct kcopyd_job *job)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Unplug the block device at the specified index.
|
||||||
|
*/
|
||||||
|
static void unplug(struct dm_kcopyd_client *kc, int rw)
|
||||||
|
{
|
||||||
|
if (kc->unplug[rw] != NULL) {
|
||||||
|
blk_unplug(bdev_get_queue(kc->unplug[rw]));
|
||||||
|
kc->unplug[rw] = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Prepare block device unplug. If there's another device
|
||||||
|
* to be unplugged at the same array index, we unplug that
|
||||||
|
* device first.
|
||||||
|
*/
|
||||||
|
static void prepare_unplug(struct dm_kcopyd_client *kc, int rw,
|
||||||
|
struct block_device *bdev)
|
||||||
|
{
|
||||||
|
if (likely(kc->unplug[rw] == bdev))
|
||||||
|
return;
|
||||||
|
unplug(kc, rw);
|
||||||
|
kc->unplug[rw] = bdev;
|
||||||
|
}
|
||||||
|
|
||||||
static void complete_io(unsigned long error, void *context)
|
static void complete_io(unsigned long error, void *context)
|
||||||
{
|
{
|
||||||
struct kcopyd_job *job = (struct kcopyd_job *) context;
|
struct kcopyd_job *job = (struct kcopyd_job *) context;
|
||||||
|
@ -345,7 +377,7 @@ static int run_io_job(struct kcopyd_job *job)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
struct dm_io_request io_req = {
|
struct dm_io_request io_req = {
|
||||||
.bi_rw = job->rw | REQ_SYNC | REQ_UNPLUG,
|
.bi_rw = job->rw,
|
||||||
.mem.type = DM_IO_PAGE_LIST,
|
.mem.type = DM_IO_PAGE_LIST,
|
||||||
.mem.ptr.pl = job->pages,
|
.mem.ptr.pl = job->pages,
|
||||||
.mem.offset = job->offset,
|
.mem.offset = job->offset,
|
||||||
|
@ -354,10 +386,16 @@ static int run_io_job(struct kcopyd_job *job)
|
||||||
.client = job->kc->io_client,
|
.client = job->kc->io_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (job->rw == READ)
|
if (job->rw == READ) {
|
||||||
r = dm_io(&io_req, 1, &job->source, NULL);
|
r = dm_io(&io_req, 1, &job->source, NULL);
|
||||||
else
|
prepare_unplug(job->kc, READ, job->source.bdev);
|
||||||
|
} else {
|
||||||
|
if (job->num_dests > 1)
|
||||||
|
io_req.bi_rw |= REQ_UNPLUG;
|
||||||
r = dm_io(&io_req, job->num_dests, job->dests, NULL);
|
r = dm_io(&io_req, job->num_dests, job->dests, NULL);
|
||||||
|
if (!(io_req.bi_rw & REQ_UNPLUG))
|
||||||
|
prepare_unplug(job->kc, WRITE, job->dests[0].bdev);
|
||||||
|
}
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -435,10 +473,18 @@ static void do_work(struct work_struct *work)
|
||||||
* Pages jobs when successful will jump onto the io jobs
|
* Pages jobs when successful will jump onto the io jobs
|
||||||
* list. io jobs call wake when they complete and it all
|
* list. io jobs call wake when they complete and it all
|
||||||
* starts again.
|
* starts again.
|
||||||
|
*
|
||||||
|
* Note that io_jobs add block devices to the unplug array,
|
||||||
|
* this array is cleared with "unplug" calls. It is thus
|
||||||
|
* forbidden to run complete_jobs after io_jobs and before
|
||||||
|
* unplug because the block device could be destroyed in
|
||||||
|
* job completion callback.
|
||||||
*/
|
*/
|
||||||
process_jobs(&kc->complete_jobs, kc, run_complete_job);
|
process_jobs(&kc->complete_jobs, kc, run_complete_job);
|
||||||
process_jobs(&kc->pages_jobs, kc, run_pages_job);
|
process_jobs(&kc->pages_jobs, kc, run_pages_job);
|
||||||
process_jobs(&kc->io_jobs, kc, run_io_job);
|
process_jobs(&kc->io_jobs, kc, run_io_job);
|
||||||
|
unplug(kc, READ);
|
||||||
|
unplug(kc, WRITE);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -619,12 +665,15 @@ int dm_kcopyd_client_create(unsigned int nr_pages,
|
||||||
INIT_LIST_HEAD(&kc->io_jobs);
|
INIT_LIST_HEAD(&kc->io_jobs);
|
||||||
INIT_LIST_HEAD(&kc->pages_jobs);
|
INIT_LIST_HEAD(&kc->pages_jobs);
|
||||||
|
|
||||||
|
memset(kc->unplug, 0, sizeof(kc->unplug));
|
||||||
|
|
||||||
kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
|
kc->job_pool = mempool_create_slab_pool(MIN_JOBS, _job_cache);
|
||||||
if (!kc->job_pool)
|
if (!kc->job_pool)
|
||||||
goto bad_slab;
|
goto bad_slab;
|
||||||
|
|
||||||
INIT_WORK(&kc->kcopyd_work, do_work);
|
INIT_WORK(&kc->kcopyd_work, do_work);
|
||||||
kc->kcopyd_wq = create_singlethread_workqueue("kcopyd");
|
kc->kcopyd_wq = alloc_workqueue("kcopyd",
|
||||||
|
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
|
||||||
if (!kc->kcopyd_wq)
|
if (!kc->kcopyd_wq)
|
||||||
goto bad_workqueue;
|
goto bad_workqueue;
|
||||||
|
|
||||||
|
|
|
@ -12,12 +12,22 @@
|
||||||
|
|
||||||
#include "dm-log-userspace-transfer.h"
|
#include "dm-log-userspace-transfer.h"
|
||||||
|
|
||||||
|
#define DM_LOG_USERSPACE_VSN "1.1.0"
|
||||||
|
|
||||||
struct flush_entry {
|
struct flush_entry {
|
||||||
int type;
|
int type;
|
||||||
region_t region;
|
region_t region;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This limit on the number of mark and clear request is, to a degree,
|
||||||
|
* arbitrary. However, there is some basis for the choice in the limits
|
||||||
|
* imposed on the size of data payload by dm-log-userspace-transfer.c:
|
||||||
|
* dm_consult_userspace().
|
||||||
|
*/
|
||||||
|
#define MAX_FLUSH_GROUP_COUNT 32
|
||||||
|
|
||||||
struct log_c {
|
struct log_c {
|
||||||
struct dm_target *ti;
|
struct dm_target *ti;
|
||||||
uint32_t region_size;
|
uint32_t region_size;
|
||||||
|
@ -37,8 +47,15 @@ struct log_c {
|
||||||
*/
|
*/
|
||||||
uint64_t in_sync_hint;
|
uint64_t in_sync_hint;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark and clear requests are held until a flush is issued
|
||||||
|
* so that we can group, and thereby limit, the amount of
|
||||||
|
* network traffic between kernel and userspace. The 'flush_lock'
|
||||||
|
* is used to protect these lists.
|
||||||
|
*/
|
||||||
spinlock_t flush_lock;
|
spinlock_t flush_lock;
|
||||||
struct list_head flush_list; /* only for clear and mark requests */
|
struct list_head mark_list;
|
||||||
|
struct list_head clear_list;
|
||||||
};
|
};
|
||||||
|
|
||||||
static mempool_t *flush_entry_pool;
|
static mempool_t *flush_entry_pool;
|
||||||
|
@ -169,7 +186,8 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
|
||||||
|
|
||||||
strncpy(lc->uuid, argv[0], DM_UUID_LEN);
|
strncpy(lc->uuid, argv[0], DM_UUID_LEN);
|
||||||
spin_lock_init(&lc->flush_lock);
|
spin_lock_init(&lc->flush_lock);
|
||||||
INIT_LIST_HEAD(&lc->flush_list);
|
INIT_LIST_HEAD(&lc->mark_list);
|
||||||
|
INIT_LIST_HEAD(&lc->clear_list);
|
||||||
|
|
||||||
str_size = build_constructor_string(ti, argc - 1, argv + 1, &ctr_str);
|
str_size = build_constructor_string(ti, argc - 1, argv + 1, &ctr_str);
|
||||||
if (str_size < 0) {
|
if (str_size < 0) {
|
||||||
|
@ -181,8 +199,11 @@ static int userspace_ctr(struct dm_dirty_log *log, struct dm_target *ti,
|
||||||
r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR,
|
r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_CTR,
|
||||||
ctr_str, str_size, NULL, NULL);
|
ctr_str, str_size, NULL, NULL);
|
||||||
|
|
||||||
if (r == -ESRCH) {
|
if (r < 0) {
|
||||||
DMERR("Userspace log server not found");
|
if (r == -ESRCH)
|
||||||
|
DMERR("Userspace log server not found");
|
||||||
|
else
|
||||||
|
DMERR("Userspace log server failed to create log");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,10 +235,9 @@ out:
|
||||||
|
|
||||||
static void userspace_dtr(struct dm_dirty_log *log)
|
static void userspace_dtr(struct dm_dirty_log *log)
|
||||||
{
|
{
|
||||||
int r;
|
|
||||||
struct log_c *lc = log->context;
|
struct log_c *lc = log->context;
|
||||||
|
|
||||||
r = dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_DTR,
|
(void) dm_consult_userspace(lc->uuid, lc->luid, DM_ULOG_DTR,
|
||||||
NULL, 0,
|
NULL, 0,
|
||||||
NULL, NULL);
|
NULL, NULL);
|
||||||
|
|
||||||
|
@ -338,6 +358,71 @@ static int userspace_in_sync(struct dm_dirty_log *log, region_t region,
|
||||||
return (r) ? 0 : (int)in_sync;
|
return (r) ? 0 : (int)in_sync;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int flush_one_by_one(struct log_c *lc, struct list_head *flush_list)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
struct flush_entry *fe;
|
||||||
|
|
||||||
|
list_for_each_entry(fe, flush_list, list) {
|
||||||
|
r = userspace_do_request(lc, lc->uuid, fe->type,
|
||||||
|
(char *)&fe->region,
|
||||||
|
sizeof(fe->region),
|
||||||
|
NULL, NULL);
|
||||||
|
if (r)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int flush_by_group(struct log_c *lc, struct list_head *flush_list)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
int count;
|
||||||
|
uint32_t type = 0;
|
||||||
|
struct flush_entry *fe, *tmp_fe;
|
||||||
|
LIST_HEAD(tmp_list);
|
||||||
|
uint64_t group[MAX_FLUSH_GROUP_COUNT];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Group process the requests
|
||||||
|
*/
|
||||||
|
while (!list_empty(flush_list)) {
|
||||||
|
count = 0;
|
||||||
|
|
||||||
|
list_for_each_entry_safe(fe, tmp_fe, flush_list, list) {
|
||||||
|
group[count] = fe->region;
|
||||||
|
count++;
|
||||||
|
|
||||||
|
list_del(&fe->list);
|
||||||
|
list_add(&fe->list, &tmp_list);
|
||||||
|
|
||||||
|
type = fe->type;
|
||||||
|
if (count >= MAX_FLUSH_GROUP_COUNT)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = userspace_do_request(lc, lc->uuid, type,
|
||||||
|
(char *)(group),
|
||||||
|
count * sizeof(uint64_t),
|
||||||
|
NULL, NULL);
|
||||||
|
if (r) {
|
||||||
|
/* Group send failed. Attempt one-by-one. */
|
||||||
|
list_splice_init(&tmp_list, flush_list);
|
||||||
|
r = flush_one_by_one(lc, flush_list);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Must collect flush_entrys that were successfully processed
|
||||||
|
* as a group so that they will be free'd by the caller.
|
||||||
|
*/
|
||||||
|
list_splice_init(&tmp_list, flush_list);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* userspace_flush
|
* userspace_flush
|
||||||
*
|
*
|
||||||
|
@ -360,31 +445,25 @@ static int userspace_flush(struct dm_dirty_log *log)
|
||||||
int r = 0;
|
int r = 0;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
struct log_c *lc = log->context;
|
struct log_c *lc = log->context;
|
||||||
LIST_HEAD(flush_list);
|
LIST_HEAD(mark_list);
|
||||||
|
LIST_HEAD(clear_list);
|
||||||
struct flush_entry *fe, *tmp_fe;
|
struct flush_entry *fe, *tmp_fe;
|
||||||
|
|
||||||
spin_lock_irqsave(&lc->flush_lock, flags);
|
spin_lock_irqsave(&lc->flush_lock, flags);
|
||||||
list_splice_init(&lc->flush_list, &flush_list);
|
list_splice_init(&lc->mark_list, &mark_list);
|
||||||
|
list_splice_init(&lc->clear_list, &clear_list);
|
||||||
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
||||||
|
|
||||||
if (list_empty(&flush_list))
|
if (list_empty(&mark_list) && list_empty(&clear_list))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
r = flush_by_group(lc, &mark_list);
|
||||||
* FIXME: Count up requests, group request types,
|
if (r)
|
||||||
* allocate memory to stick all requests in and
|
goto fail;
|
||||||
* send to server in one go. Failing the allocation,
|
|
||||||
* do it one by one.
|
|
||||||
*/
|
|
||||||
|
|
||||||
list_for_each_entry(fe, &flush_list, list) {
|
r = flush_by_group(lc, &clear_list);
|
||||||
r = userspace_do_request(lc, lc->uuid, fe->type,
|
if (r)
|
||||||
(char *)&fe->region,
|
goto fail;
|
||||||
sizeof(fe->region),
|
|
||||||
NULL, NULL);
|
|
||||||
if (r)
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
r = userspace_do_request(lc, lc->uuid, DM_ULOG_FLUSH,
|
r = userspace_do_request(lc, lc->uuid, DM_ULOG_FLUSH,
|
||||||
NULL, 0, NULL, NULL);
|
NULL, 0, NULL, NULL);
|
||||||
|
@ -395,7 +474,11 @@ fail:
|
||||||
* Calling code will receive an error and will know that
|
* Calling code will receive an error and will know that
|
||||||
* the log facility has failed.
|
* the log facility has failed.
|
||||||
*/
|
*/
|
||||||
list_for_each_entry_safe(fe, tmp_fe, &flush_list, list) {
|
list_for_each_entry_safe(fe, tmp_fe, &mark_list, list) {
|
||||||
|
list_del(&fe->list);
|
||||||
|
mempool_free(fe, flush_entry_pool);
|
||||||
|
}
|
||||||
|
list_for_each_entry_safe(fe, tmp_fe, &clear_list, list) {
|
||||||
list_del(&fe->list);
|
list_del(&fe->list);
|
||||||
mempool_free(fe, flush_entry_pool);
|
mempool_free(fe, flush_entry_pool);
|
||||||
}
|
}
|
||||||
|
@ -425,7 +508,7 @@ static void userspace_mark_region(struct dm_dirty_log *log, region_t region)
|
||||||
spin_lock_irqsave(&lc->flush_lock, flags);
|
spin_lock_irqsave(&lc->flush_lock, flags);
|
||||||
fe->type = DM_ULOG_MARK_REGION;
|
fe->type = DM_ULOG_MARK_REGION;
|
||||||
fe->region = region;
|
fe->region = region;
|
||||||
list_add(&fe->list, &lc->flush_list);
|
list_add(&fe->list, &lc->mark_list);
|
||||||
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -462,7 +545,7 @@ static void userspace_clear_region(struct dm_dirty_log *log, region_t region)
|
||||||
spin_lock_irqsave(&lc->flush_lock, flags);
|
spin_lock_irqsave(&lc->flush_lock, flags);
|
||||||
fe->type = DM_ULOG_CLEAR_REGION;
|
fe->type = DM_ULOG_CLEAR_REGION;
|
||||||
fe->region = region;
|
fe->region = region;
|
||||||
list_add(&fe->list, &lc->flush_list);
|
list_add(&fe->list, &lc->clear_list);
|
||||||
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
spin_unlock_irqrestore(&lc->flush_lock, flags);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -684,7 +767,7 @@ static int __init userspace_dirty_log_init(void)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
DMINFO("version 1.0.0 loaded");
|
DMINFO("version " DM_LOG_USERSPACE_VSN " loaded");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -694,7 +777,7 @@ static void __exit userspace_dirty_log_exit(void)
|
||||||
dm_ulog_tfr_exit();
|
dm_ulog_tfr_exit();
|
||||||
mempool_destroy(flush_entry_pool);
|
mempool_destroy(flush_entry_pool);
|
||||||
|
|
||||||
DMINFO("version 1.0.0 unloaded");
|
DMINFO("version " DM_LOG_USERSPACE_VSN " unloaded");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,7 @@ resend:
|
||||||
|
|
||||||
memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - sizeof(struct cn_msg));
|
memset(tfr, 0, DM_ULOG_PREALLOCED_SIZE - sizeof(struct cn_msg));
|
||||||
memcpy(tfr->uuid, uuid, DM_UUID_LEN);
|
memcpy(tfr->uuid, uuid, DM_UUID_LEN);
|
||||||
|
tfr->version = DM_ULOG_REQUEST_VERSION;
|
||||||
tfr->luid = luid;
|
tfr->luid = luid;
|
||||||
tfr->seq = dm_ulog_seq++;
|
tfr->seq = dm_ulog_seq++;
|
||||||
|
|
||||||
|
|
|
@ -455,7 +455,7 @@ static int create_log_context(struct dm_dirty_log *log, struct dm_target *ti,
|
||||||
r = PTR_ERR(lc->io_req.client);
|
r = PTR_ERR(lc->io_req.client);
|
||||||
DMWARN("couldn't allocate disk io client");
|
DMWARN("couldn't allocate disk io client");
|
||||||
kfree(lc);
|
kfree(lc);
|
||||||
return -ENOMEM;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
lc->disk_header = vmalloc(buf_size);
|
lc->disk_header = vmalloc(buf_size);
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
|
|
||||||
#define DM_MSG_PREFIX "multipath"
|
#define DM_MSG_PREFIX "multipath"
|
||||||
#define MESG_STR(x) x, sizeof(x)
|
#define MESG_STR(x) x, sizeof(x)
|
||||||
|
#define DM_PG_INIT_DELAY_MSECS 2000
|
||||||
|
#define DM_PG_INIT_DELAY_DEFAULT ((unsigned) -1)
|
||||||
|
|
||||||
/* Path properties */
|
/* Path properties */
|
||||||
struct pgpath {
|
struct pgpath {
|
||||||
|
@ -33,8 +35,7 @@ struct pgpath {
|
||||||
unsigned fail_count; /* Cumulative failure count */
|
unsigned fail_count; /* Cumulative failure count */
|
||||||
|
|
||||||
struct dm_path path;
|
struct dm_path path;
|
||||||
struct work_struct deactivate_path;
|
struct delayed_work activate_path;
|
||||||
struct work_struct activate_path;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
|
#define path_to_pgpath(__pgp) container_of((__pgp), struct pgpath, path)
|
||||||
|
@ -65,11 +66,15 @@ struct multipath {
|
||||||
|
|
||||||
const char *hw_handler_name;
|
const char *hw_handler_name;
|
||||||
char *hw_handler_params;
|
char *hw_handler_params;
|
||||||
|
|
||||||
unsigned nr_priority_groups;
|
unsigned nr_priority_groups;
|
||||||
struct list_head priority_groups;
|
struct list_head priority_groups;
|
||||||
|
|
||||||
|
wait_queue_head_t pg_init_wait; /* Wait for pg_init completion */
|
||||||
|
|
||||||
unsigned pg_init_required; /* pg_init needs calling? */
|
unsigned pg_init_required; /* pg_init needs calling? */
|
||||||
unsigned pg_init_in_progress; /* Only one pg_init allowed at once */
|
unsigned pg_init_in_progress; /* Only one pg_init allowed at once */
|
||||||
wait_queue_head_t pg_init_wait; /* Wait for pg_init completion */
|
unsigned pg_init_delay_retry; /* Delay pg_init retry? */
|
||||||
|
|
||||||
unsigned nr_valid_paths; /* Total number of usable paths */
|
unsigned nr_valid_paths; /* Total number of usable paths */
|
||||||
struct pgpath *current_pgpath;
|
struct pgpath *current_pgpath;
|
||||||
|
@ -82,6 +87,7 @@ struct multipath {
|
||||||
unsigned saved_queue_if_no_path;/* Saved state during suspension */
|
unsigned saved_queue_if_no_path;/* Saved state during suspension */
|
||||||
unsigned pg_init_retries; /* Number of times to retry pg_init */
|
unsigned pg_init_retries; /* Number of times to retry pg_init */
|
||||||
unsigned pg_init_count; /* Number of times pg_init called */
|
unsigned pg_init_count; /* Number of times pg_init called */
|
||||||
|
unsigned pg_init_delay_msecs; /* Number of msecs before pg_init retry */
|
||||||
|
|
||||||
struct work_struct process_queued_ios;
|
struct work_struct process_queued_ios;
|
||||||
struct list_head queued_ios;
|
struct list_head queued_ios;
|
||||||
|
@ -116,7 +122,6 @@ static struct workqueue_struct *kmultipathd, *kmpath_handlerd;
|
||||||
static void process_queued_ios(struct work_struct *work);
|
static void process_queued_ios(struct work_struct *work);
|
||||||
static void trigger_event(struct work_struct *work);
|
static void trigger_event(struct work_struct *work);
|
||||||
static void activate_path(struct work_struct *work);
|
static void activate_path(struct work_struct *work);
|
||||||
static void deactivate_path(struct work_struct *work);
|
|
||||||
|
|
||||||
|
|
||||||
/*-----------------------------------------------
|
/*-----------------------------------------------
|
||||||
|
@ -129,8 +134,7 @@ static struct pgpath *alloc_pgpath(void)
|
||||||
|
|
||||||
if (pgpath) {
|
if (pgpath) {
|
||||||
pgpath->is_active = 1;
|
pgpath->is_active = 1;
|
||||||
INIT_WORK(&pgpath->deactivate_path, deactivate_path);
|
INIT_DELAYED_WORK(&pgpath->activate_path, activate_path);
|
||||||
INIT_WORK(&pgpath->activate_path, activate_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return pgpath;
|
return pgpath;
|
||||||
|
@ -141,14 +145,6 @@ static void free_pgpath(struct pgpath *pgpath)
|
||||||
kfree(pgpath);
|
kfree(pgpath);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deactivate_path(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct pgpath *pgpath =
|
|
||||||
container_of(work, struct pgpath, deactivate_path);
|
|
||||||
|
|
||||||
blk_abort_queue(pgpath->path.dev->bdev->bd_disk->queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct priority_group *alloc_priority_group(void)
|
static struct priority_group *alloc_priority_group(void)
|
||||||
{
|
{
|
||||||
struct priority_group *pg;
|
struct priority_group *pg;
|
||||||
|
@ -199,6 +195,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
|
||||||
INIT_LIST_HEAD(&m->queued_ios);
|
INIT_LIST_HEAD(&m->queued_ios);
|
||||||
spin_lock_init(&m->lock);
|
spin_lock_init(&m->lock);
|
||||||
m->queue_io = 1;
|
m->queue_io = 1;
|
||||||
|
m->pg_init_delay_msecs = DM_PG_INIT_DELAY_DEFAULT;
|
||||||
INIT_WORK(&m->process_queued_ios, process_queued_ios);
|
INIT_WORK(&m->process_queued_ios, process_queued_ios);
|
||||||
INIT_WORK(&m->trigger_event, trigger_event);
|
INIT_WORK(&m->trigger_event, trigger_event);
|
||||||
init_waitqueue_head(&m->pg_init_wait);
|
init_waitqueue_head(&m->pg_init_wait);
|
||||||
|
@ -238,14 +235,19 @@ static void free_multipath(struct multipath *m)
|
||||||
static void __pg_init_all_paths(struct multipath *m)
|
static void __pg_init_all_paths(struct multipath *m)
|
||||||
{
|
{
|
||||||
struct pgpath *pgpath;
|
struct pgpath *pgpath;
|
||||||
|
unsigned long pg_init_delay = 0;
|
||||||
|
|
||||||
m->pg_init_count++;
|
m->pg_init_count++;
|
||||||
m->pg_init_required = 0;
|
m->pg_init_required = 0;
|
||||||
|
if (m->pg_init_delay_retry)
|
||||||
|
pg_init_delay = msecs_to_jiffies(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT ?
|
||||||
|
m->pg_init_delay_msecs : DM_PG_INIT_DELAY_MSECS);
|
||||||
list_for_each_entry(pgpath, &m->current_pg->pgpaths, list) {
|
list_for_each_entry(pgpath, &m->current_pg->pgpaths, list) {
|
||||||
/* Skip failed paths */
|
/* Skip failed paths */
|
||||||
if (!pgpath->is_active)
|
if (!pgpath->is_active)
|
||||||
continue;
|
continue;
|
||||||
if (queue_work(kmpath_handlerd, &pgpath->activate_path))
|
if (queue_delayed_work(kmpath_handlerd, &pgpath->activate_path,
|
||||||
|
pg_init_delay))
|
||||||
m->pg_init_in_progress++;
|
m->pg_init_in_progress++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -793,8 +795,9 @@ static int parse_features(struct arg_set *as, struct multipath *m)
|
||||||
const char *param_name;
|
const char *param_name;
|
||||||
|
|
||||||
static struct param _params[] = {
|
static struct param _params[] = {
|
||||||
{0, 3, "invalid number of feature args"},
|
{0, 5, "invalid number of feature args"},
|
||||||
{1, 50, "pg_init_retries must be between 1 and 50"},
|
{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 = read_param(_params, shift(as), &argc, &ti->error);
|
||||||
|
@ -821,6 +824,14 @@ static int parse_features(struct arg_set *as, struct multipath *m)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!strnicmp(param_name, MESG_STR("pg_init_delay_msecs")) &&
|
||||||
|
(argc >= 1)) {
|
||||||
|
r = read_param(_params + 2, shift(as),
|
||||||
|
&m->pg_init_delay_msecs, &ti->error);
|
||||||
|
argc--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ti->error = "Unrecognised multipath feature request";
|
ti->error = "Unrecognised multipath feature request";
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
} while (argc && !r);
|
} while (argc && !r);
|
||||||
|
@ -931,7 +942,7 @@ static void flush_multipath_work(struct multipath *m)
|
||||||
flush_workqueue(kmpath_handlerd);
|
flush_workqueue(kmpath_handlerd);
|
||||||
multipath_wait_for_pg_init_completion(m);
|
multipath_wait_for_pg_init_completion(m);
|
||||||
flush_workqueue(kmultipathd);
|
flush_workqueue(kmultipathd);
|
||||||
flush_scheduled_work();
|
flush_work_sync(&m->trigger_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void multipath_dtr(struct dm_target *ti)
|
static void multipath_dtr(struct dm_target *ti)
|
||||||
|
@ -995,7 +1006,6 @@ static int fail_path(struct pgpath *pgpath)
|
||||||
pgpath->path.dev->name, m->nr_valid_paths);
|
pgpath->path.dev->name, m->nr_valid_paths);
|
||||||
|
|
||||||
schedule_work(&m->trigger_event);
|
schedule_work(&m->trigger_event);
|
||||||
queue_work(kmultipathd, &pgpath->deactivate_path);
|
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&m->lock, flags);
|
spin_unlock_irqrestore(&m->lock, flags);
|
||||||
|
@ -1034,7 +1044,7 @@ static int reinstate_path(struct pgpath *pgpath)
|
||||||
m->current_pgpath = NULL;
|
m->current_pgpath = NULL;
|
||||||
queue_work(kmultipathd, &m->process_queued_ios);
|
queue_work(kmultipathd, &m->process_queued_ios);
|
||||||
} else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
|
} else if (m->hw_handler_name && (m->current_pg == pgpath->pg)) {
|
||||||
if (queue_work(kmpath_handlerd, &pgpath->activate_path))
|
if (queue_work(kmpath_handlerd, &pgpath->activate_path.work))
|
||||||
m->pg_init_in_progress++;
|
m->pg_init_in_progress++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,6 +1179,7 @@ static void pg_init_done(void *data, int errors)
|
||||||
struct priority_group *pg = pgpath->pg;
|
struct priority_group *pg = pgpath->pg;
|
||||||
struct multipath *m = pg->m;
|
struct multipath *m = pg->m;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
unsigned delay_retry = 0;
|
||||||
|
|
||||||
/* device or driver problems */
|
/* device or driver problems */
|
||||||
switch (errors) {
|
switch (errors) {
|
||||||
|
@ -1193,8 +1204,9 @@ static void pg_init_done(void *data, int errors)
|
||||||
*/
|
*/
|
||||||
bypass_pg(m, pg, 1);
|
bypass_pg(m, pg, 1);
|
||||||
break;
|
break;
|
||||||
/* TODO: For SCSI_DH_RETRY we should wait a couple seconds */
|
|
||||||
case SCSI_DH_RETRY:
|
case SCSI_DH_RETRY:
|
||||||
|
/* Wait before retrying. */
|
||||||
|
delay_retry = 1;
|
||||||
case SCSI_DH_IMM_RETRY:
|
case SCSI_DH_IMM_RETRY:
|
||||||
case SCSI_DH_RES_TEMP_UNAVAIL:
|
case SCSI_DH_RES_TEMP_UNAVAIL:
|
||||||
if (pg_init_limit_reached(m, pgpath))
|
if (pg_init_limit_reached(m, pgpath))
|
||||||
|
@ -1227,6 +1239,7 @@ static void pg_init_done(void *data, int errors)
|
||||||
if (!m->pg_init_required)
|
if (!m->pg_init_required)
|
||||||
m->queue_io = 0;
|
m->queue_io = 0;
|
||||||
|
|
||||||
|
m->pg_init_delay_retry = delay_retry;
|
||||||
queue_work(kmultipathd, &m->process_queued_ios);
|
queue_work(kmultipathd, &m->process_queued_ios);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1241,7 +1254,7 @@ out:
|
||||||
static void activate_path(struct work_struct *work)
|
static void activate_path(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct pgpath *pgpath =
|
struct pgpath *pgpath =
|
||||||
container_of(work, struct pgpath, activate_path);
|
container_of(work, struct pgpath, activate_path.work);
|
||||||
|
|
||||||
scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
|
scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
|
||||||
pg_init_done, pgpath);
|
pg_init_done, pgpath);
|
||||||
|
@ -1382,11 +1395,14 @@ static int multipath_status(struct dm_target *ti, status_type_t type,
|
||||||
DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
|
DMEMIT("2 %u %u ", m->queue_size, m->pg_init_count);
|
||||||
else {
|
else {
|
||||||
DMEMIT("%u ", m->queue_if_no_path +
|
DMEMIT("%u ", m->queue_if_no_path +
|
||||||
(m->pg_init_retries > 0) * 2);
|
(m->pg_init_retries > 0) * 2 +
|
||||||
|
(m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT) * 2);
|
||||||
if (m->queue_if_no_path)
|
if (m->queue_if_no_path)
|
||||||
DMEMIT("queue_if_no_path ");
|
DMEMIT("queue_if_no_path ");
|
||||||
if (m->pg_init_retries)
|
if (m->pg_init_retries)
|
||||||
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
|
DMEMIT("pg_init_retries %u ", m->pg_init_retries);
|
||||||
|
if (m->pg_init_delay_msecs != DM_PG_INIT_DELAY_DEFAULT)
|
||||||
|
DMEMIT("pg_init_delay_msecs %u ", m->pg_init_delay_msecs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!m->hw_handler_name || type == STATUSTYPE_INFO)
|
if (!m->hw_handler_name || type == STATUSTYPE_INFO)
|
||||||
|
@ -1655,7 +1671,7 @@ out:
|
||||||
*---------------------------------------------------------------*/
|
*---------------------------------------------------------------*/
|
||||||
static struct target_type multipath_target = {
|
static struct target_type multipath_target = {
|
||||||
.name = "multipath",
|
.name = "multipath",
|
||||||
.version = {1, 1, 1},
|
.version = {1, 2, 0},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = multipath_ctr,
|
.ctr = multipath_ctr,
|
||||||
.dtr = multipath_dtr,
|
.dtr = multipath_dtr,
|
||||||
|
@ -1687,7 +1703,7 @@ static int __init dm_multipath_init(void)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
kmultipathd = create_workqueue("kmpathd");
|
kmultipathd = alloc_workqueue("kmpathd", WQ_MEM_RECLAIM, 0);
|
||||||
if (!kmultipathd) {
|
if (!kmultipathd) {
|
||||||
DMERR("failed to create workqueue kmpathd");
|
DMERR("failed to create workqueue kmpathd");
|
||||||
dm_unregister_target(&multipath_target);
|
dm_unregister_target(&multipath_target);
|
||||||
|
@ -1701,7 +1717,8 @@ static int __init dm_multipath_init(void)
|
||||||
* old workqueue would also create a bottleneck in the
|
* old workqueue would also create a bottleneck in the
|
||||||
* path of the storage hardware device activation.
|
* path of the storage hardware device activation.
|
||||||
*/
|
*/
|
||||||
kmpath_handlerd = create_singlethread_workqueue("kmpath_handlerd");
|
kmpath_handlerd = alloc_ordered_workqueue("kmpath_handlerd",
|
||||||
|
WQ_MEM_RECLAIM);
|
||||||
if (!kmpath_handlerd) {
|
if (!kmpath_handlerd) {
|
||||||
DMERR("failed to create workqueue kmpath_handlerd");
|
DMERR("failed to create workqueue kmpath_handlerd");
|
||||||
destroy_workqueue(kmultipathd);
|
destroy_workqueue(kmultipathd);
|
||||||
|
|
697
drivers/md/dm-raid.c
Normal file
697
drivers/md/dm-raid.c
Normal file
|
@ -0,0 +1,697 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2010-2011 Neil Brown
|
||||||
|
* Copyright (C) 2010-2011 Red Hat, Inc. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is released under the GPL.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include "md.h"
|
||||||
|
#include "raid5.h"
|
||||||
|
#include "dm.h"
|
||||||
|
#include "bitmap.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.
|
||||||
|
*/
|
||||||
|
#ifndef MD_SYNC_STATE_FORCED
|
||||||
|
#define MD_SYNC_STATE_FORCED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct raid_dev {
|
||||||
|
/*
|
||||||
|
* Two DM devices, one to hold metadata and one to hold the
|
||||||
|
* actual data/parity. The reason for this is to not confuse
|
||||||
|
* ti->len and give more flexibility in altering size and
|
||||||
|
* characteristics.
|
||||||
|
*
|
||||||
|
* While it is possible for this device to be associated
|
||||||
|
* with a different physical device than the data_dev, it
|
||||||
|
* is intended for it to be the same.
|
||||||
|
* |--------- Physical Device ---------|
|
||||||
|
* |- meta_dev -|------ data_dev ------|
|
||||||
|
*/
|
||||||
|
struct dm_dev *meta_dev;
|
||||||
|
struct dm_dev *data_dev;
|
||||||
|
struct mdk_rdev_s rdev;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
struct raid_set {
|
||||||
|
struct dm_target *ti;
|
||||||
|
|
||||||
|
uint64_t print_flags;
|
||||||
|
|
||||||
|
struct mddev_s md;
|
||||||
|
struct raid_type *raid_type;
|
||||||
|
struct dm_target_callbacks callbacks;
|
||||||
|
|
||||||
|
struct raid_dev dev[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Supported raid types and properties. */
|
||||||
|
static struct raid_type {
|
||||||
|
const char *name; /* RAID algorithm. */
|
||||||
|
const char *descr; /* Descriptor text for logging. */
|
||||||
|
const unsigned parity_devs; /* # of parity devices. */
|
||||||
|
const unsigned minimal_devs; /* minimal # of devices in set. */
|
||||||
|
const unsigned level; /* RAID level. */
|
||||||
|
const unsigned algorithm; /* RAID algorithm. */
|
||||||
|
} raid_types[] = {
|
||||||
|
{"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},
|
||||||
|
{"raid5_ls", "RAID5 (left symmetric)", 1, 2, 5, ALGORITHM_LEFT_SYMMETRIC},
|
||||||
|
{"raid5_rs", "RAID5 (right symmetric)", 1, 2, 5, ALGORITHM_RIGHT_SYMMETRIC},
|
||||||
|
{"raid6_zr", "RAID6 (zero restart)", 2, 4, 6, ALGORITHM_ROTATING_ZERO_RESTART},
|
||||||
|
{"raid6_nr", "RAID6 (N restart)", 2, 4, 6, ALGORITHM_ROTATING_N_RESTART},
|
||||||
|
{"raid6_nc", "RAID6 (N continue)", 2, 4, 6, ALGORITHM_ROTATING_N_CONTINUE}
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct raid_type *get_raid_type(char *name)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(raid_types); i++)
|
||||||
|
if (!strcmp(raid_types[i].name, name))
|
||||||
|
return &raid_types[i];
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct raid_set *context_alloc(struct dm_target *ti, struct raid_type *raid_type, unsigned raid_devs)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
struct raid_set *rs;
|
||||||
|
sector_t sectors_per_dev;
|
||||||
|
|
||||||
|
if (raid_devs <= raid_type->parity_devs) {
|
||||||
|
ti->error = "Insufficient number of devices";
|
||||||
|
return ERR_PTR(-EINVAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
sectors_per_dev = ti->len;
|
||||||
|
if (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);
|
||||||
|
}
|
||||||
|
|
||||||
|
rs = kzalloc(sizeof(*rs) + raid_devs * sizeof(rs->dev[0]), GFP_KERNEL);
|
||||||
|
if (!rs) {
|
||||||
|
ti->error = "Cannot allocate raid context";
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
}
|
||||||
|
|
||||||
|
mddev_init(&rs->md);
|
||||||
|
|
||||||
|
rs->ti = ti;
|
||||||
|
rs->raid_type = raid_type;
|
||||||
|
rs->md.raid_disks = raid_devs;
|
||||||
|
rs->md.level = raid_type->level;
|
||||||
|
rs->md.new_level = rs->md.level;
|
||||||
|
rs->md.dev_sectors = sectors_per_dev;
|
||||||
|
rs->md.layout = raid_type->algorithm;
|
||||||
|
rs->md.new_layout = rs->md.layout;
|
||||||
|
rs->md.delta_disks = 0;
|
||||||
|
rs->md.recovery_cp = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < raid_devs; i++)
|
||||||
|
md_rdev_init(&rs->dev[i].rdev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Remaining items to be initialized by further RAID params:
|
||||||
|
* rs->md.persistent
|
||||||
|
* rs->md.external
|
||||||
|
* rs->md.chunk_sectors
|
||||||
|
* rs->md.new_chunk_sectors
|
||||||
|
*/
|
||||||
|
|
||||||
|
return rs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void context_free(struct raid_set *rs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++)
|
||||||
|
if (rs->dev[i].data_dev)
|
||||||
|
dm_put_device(rs->ti, rs->dev[i].data_dev);
|
||||||
|
|
||||||
|
kfree(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For every device we have two words
|
||||||
|
* <meta_dev>: meta device name or '-' if missing
|
||||||
|
* <data_dev>: data device name or '-' if missing
|
||||||
|
*
|
||||||
|
* This code parses those words.
|
||||||
|
*/
|
||||||
|
static int dev_parms(struct raid_set *rs, char **argv)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
int rebuild = 0;
|
||||||
|
int metadata_available = 0;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++, argv += 2) {
|
||||||
|
rs->dev[i].rdev.raid_disk = i;
|
||||||
|
|
||||||
|
rs->dev[i].meta_dev = NULL;
|
||||||
|
rs->dev[i].data_dev = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* There are no offsets, since there is a separate device
|
||||||
|
* for data and metadata.
|
||||||
|
*/
|
||||||
|
rs->dev[i].rdev.data_offset = 0;
|
||||||
|
rs->dev[i].rdev.mddev = &rs->md;
|
||||||
|
|
||||||
|
if (strcmp(argv[0], "-")) {
|
||||||
|
rs->ti->error = "Metadata devices not supported";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(argv[1], "-")) {
|
||||||
|
if (!test_bit(In_sync, &rs->dev[i].rdev.flags) &&
|
||||||
|
(!rs->dev[i].rdev.recovery_offset)) {
|
||||||
|
rs->ti->error = "Drive designated for rebuild not specified";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dm_get_device(rs->ti, argv[1],
|
||||||
|
dm_table_get_mode(rs->ti->table),
|
||||||
|
&rs->dev[i].data_dev);
|
||||||
|
if (ret) {
|
||||||
|
rs->ti->error = "RAID device lookup failure";
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
rebuild++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata_available) {
|
||||||
|
rs->md.external = 0;
|
||||||
|
rs->md.persistent = 1;
|
||||||
|
rs->md.major_version = 2;
|
||||||
|
} else if (rebuild && !rs->md.recovery_cp) {
|
||||||
|
/*
|
||||||
|
* Without metadata, we will not be able to tell if the array
|
||||||
|
* is in-sync or not - we must assume it is not. Therefore,
|
||||||
|
* it is impossible to rebuild a drive.
|
||||||
|
*
|
||||||
|
* Even if there is metadata, the on-disk information may
|
||||||
|
* indicate that the array is not in-sync and it will then
|
||||||
|
* fail at that time.
|
||||||
|
*
|
||||||
|
* User could specify 'nosync' option if desperate.
|
||||||
|
*/
|
||||||
|
DMERR("Unable to rebuild drive while array is not in-sync");
|
||||||
|
rs->ti->error = "RAID device lookup failure";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Possible arguments are...
|
||||||
|
* RAID456:
|
||||||
|
* <chunk_size> [optional_args]
|
||||||
|
*
|
||||||
|
* Optional args:
|
||||||
|
* [[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
|
||||||
|
* [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
|
||||||
|
*/
|
||||||
|
static int parse_raid_params(struct raid_set *rs, char **argv,
|
||||||
|
unsigned num_raid_params)
|
||||||
|
{
|
||||||
|
unsigned i, rebuild_cnt = 0;
|
||||||
|
unsigned long value;
|
||||||
|
char *key;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First, parse the in-order required arguments
|
||||||
|
*/
|
||||||
|
if ((strict_strtoul(argv[0], 10, &value) < 0) ||
|
||||||
|
!is_power_of_2(value) || (value < 8)) {
|
||||||
|
rs->ti->error = "Bad chunk size";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->md.new_chunk_sectors = rs->md.chunk_sectors = value;
|
||||||
|
argv++;
|
||||||
|
num_raid_params--;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Second, parse the unordered optional arguments
|
||||||
|
*/
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++)
|
||||||
|
set_bit(In_sync, &rs->dev[i].rdev.flags);
|
||||||
|
|
||||||
|
for (i = 0; i < num_raid_params; i++) {
|
||||||
|
if (!strcmp(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")) {
|
||||||
|
rs->md.recovery_cp = 0;
|
||||||
|
rs->print_flags |= DMPF_SYNC;
|
||||||
|
rs->md.flags |= MD_SYNC_STATE_FORCED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The rest of the optional arguments come in key/value pairs */
|
||||||
|
if ((i + 1) >= num_raid_params) {
|
||||||
|
rs->ti->error = "Wrong number of raid parameters given";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
key = argv[i++];
|
||||||
|
if (strict_strtoul(argv[i], 10, &value) < 0) {
|
||||||
|
rs->ti->error = "Bad numerical argument given in raid params";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!strcmp(key, "rebuild")) {
|
||||||
|
if (++rebuild_cnt > rs->raid_type->parity_devs) {
|
||||||
|
rs->ti->error = "Too many rebuild drives given";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (value > rs->md.raid_disks) {
|
||||||
|
rs->ti->error = "Invalid rebuild index given";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
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_MAX_WRITE_BEHIND;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In device-mapper, we specify things in sectors, but
|
||||||
|
* MD records this value in kB
|
||||||
|
*/
|
||||||
|
value /= 2;
|
||||||
|
if (value > COUNTER_MAX) {
|
||||||
|
rs->ti->error = "Max write-behind limit out of range";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
rs->md.bitmap_info.max_write_behind = value;
|
||||||
|
} else if (!strcmp(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")) {
|
||||||
|
rs->print_flags |= DMPF_STRIPE_CACHE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In device-mapper, we specify things in sectors, but
|
||||||
|
* MD records this value in kB
|
||||||
|
*/
|
||||||
|
value /= 2;
|
||||||
|
|
||||||
|
if (rs->raid_type->level < 5) {
|
||||||
|
rs->ti->error = "Inappropriate argument: stripe_cache";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
if (raid5_set_cache_size(&rs->md, (int)value)) {
|
||||||
|
rs->ti->error = "Bad stripe_cache size";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
} else if (!strcmp(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")) {
|
||||||
|
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 {
|
||||||
|
DMERR("Unable to parse RAID parameter: %s", key);
|
||||||
|
rs->ti->error = "Unable to parse RAID parameters";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assume there are no metadata devices until the drives are parsed */
|
||||||
|
rs->md.persistent = 0;
|
||||||
|
rs->md.external = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void do_table_event(struct work_struct *ws)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = container_of(ws, struct raid_set, md.event_work);
|
||||||
|
|
||||||
|
dm_table_event(rs->ti->table);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raid_is_congested(struct dm_target_callbacks *cb, int bits)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
|
||||||
|
|
||||||
|
return md_raid5_congested(&rs->md, bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_unplug(struct dm_target_callbacks *cb)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = container_of(cb, struct raid_set, callbacks);
|
||||||
|
|
||||||
|
md_raid5_unplug_device(rs->md.private);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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>.
|
||||||
|
*/
|
||||||
|
static int raid_ctr(struct dm_target *ti, unsigned argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
struct raid_type *rt;
|
||||||
|
unsigned long num_raid_params, num_raid_devs;
|
||||||
|
struct raid_set *rs = NULL;
|
||||||
|
|
||||||
|
/* Must have at least <raid_type> <#raid_params> */
|
||||||
|
if (argc < 2) {
|
||||||
|
ti->error = "Too few arguments";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* raid type */
|
||||||
|
rt = get_raid_type(argv[0]);
|
||||||
|
if (!rt) {
|
||||||
|
ti->error = "Unrecognised raid_type";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
/* number of RAID parameters */
|
||||||
|
if (strict_strtoul(argv[0], 10, &num_raid_params) < 0) {
|
||||||
|
ti->error = "Cannot understand number of RAID parameters";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
argc--;
|
||||||
|
argv++;
|
||||||
|
|
||||||
|
/* Skip over RAID params for now and find out # of devices */
|
||||||
|
if (num_raid_params + 1 > argc) {
|
||||||
|
ti->error = "Arguments do not agree with counts given";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((strict_strtoul(argv[num_raid_params], 10, &num_raid_devs) < 0) ||
|
||||||
|
(num_raid_devs >= INT_MAX)) {
|
||||||
|
ti->error = "Cannot understand number of raid devices";
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs = context_alloc(ti, rt, (unsigned)num_raid_devs);
|
||||||
|
if (IS_ERR(rs))
|
||||||
|
return PTR_ERR(rs);
|
||||||
|
|
||||||
|
ret = parse_raid_params(rs, argv, (unsigned)num_raid_params);
|
||||||
|
if (ret)
|
||||||
|
goto bad;
|
||||||
|
|
||||||
|
ret = -EINVAL;
|
||||||
|
|
||||||
|
argc -= num_raid_params + 1; /* +1: we already have num_raid_devs */
|
||||||
|
argv += num_raid_params + 1;
|
||||||
|
|
||||||
|
if (argc != (num_raid_devs * 2)) {
|
||||||
|
ti->error = "Supplied RAID devices does not match the count given";
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dev_parms(rs, argv);
|
||||||
|
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);
|
||||||
|
ret = md_run(&rs->md);
|
||||||
|
rs->md.in_sync = 0; /* Assume already marked dirty */
|
||||||
|
mutex_unlock(&rs->md.reconfig_mutex);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
ti->error = "Fail to run raid array";
|
||||||
|
goto bad;
|
||||||
|
}
|
||||||
|
|
||||||
|
rs->callbacks.congested_fn = raid_is_congested;
|
||||||
|
rs->callbacks.unplug_fn = raid_unplug;
|
||||||
|
dm_table_add_target_callbacks(ti->table, &rs->callbacks);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
bad:
|
||||||
|
context_free(rs);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_dtr(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
|
||||||
|
list_del_init(&rs->callbacks.list);
|
||||||
|
md_stop(&rs->md);
|
||||||
|
context_free(rs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_context)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
mddev_t *mddev = &rs->md;
|
||||||
|
|
||||||
|
mddev->pers->make_request(mddev, bio);
|
||||||
|
|
||||||
|
return DM_MAPIO_SUBMITTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raid_status(struct dm_target *ti, status_type_t type,
|
||||||
|
char *result, unsigned maxlen)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
unsigned raid_param_cnt = 1; /* at least 1 for chunksize */
|
||||||
|
unsigned sz = 0;
|
||||||
|
int i;
|
||||||
|
sector_t sync;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case STATUSTYPE_INFO:
|
||||||
|
DMEMIT("%s %d ", rs->raid_type->name, rs->md.raid_disks);
|
||||||
|
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++) {
|
||||||
|
if (test_bit(Faulty, &rs->dev[i].rdev.flags))
|
||||||
|
DMEMIT("D");
|
||||||
|
else if (test_bit(In_sync, &rs->dev[i].rdev.flags))
|
||||||
|
DMEMIT("A");
|
||||||
|
else
|
||||||
|
DMEMIT("a");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (test_bit(MD_RECOVERY_RUNNING, &rs->md.recovery))
|
||||||
|
sync = rs->md.curr_resync_completed;
|
||||||
|
else
|
||||||
|
sync = rs->md.recovery_cp;
|
||||||
|
|
||||||
|
if (sync > rs->md.resync_max_sectors)
|
||||||
|
sync = rs->md.resync_max_sectors;
|
||||||
|
|
||||||
|
DMEMIT(" %llu/%llu",
|
||||||
|
(unsigned long long) sync,
|
||||||
|
(unsigned long long) rs->md.resync_max_sectors);
|
||||||
|
|
||||||
|
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 &&
|
||||||
|
!test_bit(In_sync, &rs->dev[i].rdev.flags))
|
||||||
|
raid_param_cnt++; /* for rebuilds */
|
||||||
|
|
||||||
|
raid_param_cnt += (hweight64(rs->print_flags) * 2);
|
||||||
|
if (rs->print_flags & (DMPF_SYNC | DMPF_NOSYNC))
|
||||||
|
raid_param_cnt--;
|
||||||
|
|
||||||
|
DMEMIT("%s %u %u", rs->raid_type->name,
|
||||||
|
raid_param_cnt, rs->md.chunk_sectors);
|
||||||
|
|
||||||
|
if ((rs->print_flags & DMPF_SYNC) &&
|
||||||
|
(rs->md.recovery_cp == MaxSector))
|
||||||
|
DMEMIT(" sync");
|
||||||
|
if (rs->print_flags & DMPF_NOSYNC)
|
||||||
|
DMEMIT(" nosync");
|
||||||
|
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++)
|
||||||
|
if (rs->dev[i].data_dev &&
|
||||||
|
!test_bit(In_sync, &rs->dev[i].rdev.flags))
|
||||||
|
DMEMIT(" rebuild %u", i);
|
||||||
|
|
||||||
|
if (rs->print_flags & DMPF_DAEMON_SLEEP)
|
||||||
|
DMEMIT(" daemon_sleep %lu",
|
||||||
|
rs->md.bitmap_info.daemon_sleep);
|
||||||
|
|
||||||
|
if (rs->print_flags & DMPF_MIN_RECOVERY_RATE)
|
||||||
|
DMEMIT(" min_recovery_rate %d", rs->md.sync_speed_min);
|
||||||
|
|
||||||
|
if (rs->print_flags & DMPF_MAX_RECOVERY_RATE)
|
||||||
|
DMEMIT(" max_recovery_rate %d", rs->md.sync_speed_max);
|
||||||
|
|
||||||
|
if (rs->print_flags & DMPF_MAX_WRITE_BEHIND)
|
||||||
|
DMEMIT(" max_write_behind %lu",
|
||||||
|
rs->md.bitmap_info.max_write_behind);
|
||||||
|
|
||||||
|
if (rs->print_flags & DMPF_STRIPE_CACHE) {
|
||||||
|
raid5_conf_t *conf = rs->md.private;
|
||||||
|
|
||||||
|
/* convert from kiB to sectors */
|
||||||
|
DMEMIT(" stripe_cache %d",
|
||||||
|
conf ? conf->max_nr_stripes * 2 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
DMEMIT(" %d", rs->md.raid_disks);
|
||||||
|
for (i = 0; i < rs->md.raid_disks; i++) {
|
||||||
|
DMEMIT(" -"); /* metadata device */
|
||||||
|
|
||||||
|
if (rs->dev[i].data_dev)
|
||||||
|
DMEMIT(" %s", rs->dev[i].data_dev->name);
|
||||||
|
else
|
||||||
|
DMEMIT(" -");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int raid_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
unsigned i;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
for (i = 0; !ret && i < rs->md.raid_disks; i++)
|
||||||
|
if (rs->dev[i].data_dev)
|
||||||
|
ret = fn(ti,
|
||||||
|
rs->dev[i].data_dev,
|
||||||
|
0, /* No offset on data devs */
|
||||||
|
rs->md.dev_sectors,
|
||||||
|
data);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_io_hints(struct dm_target *ti, struct queue_limits *limits)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
unsigned chunk_size = rs->md.chunk_sectors << 9;
|
||||||
|
raid5_conf_t *conf = rs->md.private;
|
||||||
|
|
||||||
|
blk_limits_io_min(limits, chunk_size);
|
||||||
|
blk_limits_io_opt(limits, chunk_size * (conf->raid_disks - conf->max_degraded));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_presuspend(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
|
||||||
|
md_stop_writes(&rs->md);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_postsuspend(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
|
||||||
|
mddev_suspend(&rs->md);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void raid_resume(struct dm_target *ti)
|
||||||
|
{
|
||||||
|
struct raid_set *rs = ti->private;
|
||||||
|
|
||||||
|
mddev_resume(&rs->md);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct target_type raid_target = {
|
||||||
|
.name = "raid",
|
||||||
|
.version = {1, 0, 0},
|
||||||
|
.module = THIS_MODULE,
|
||||||
|
.ctr = raid_ctr,
|
||||||
|
.dtr = raid_dtr,
|
||||||
|
.map = raid_map,
|
||||||
|
.status = raid_status,
|
||||||
|
.iterate_devices = raid_iterate_devices,
|
||||||
|
.io_hints = raid_io_hints,
|
||||||
|
.presuspend = raid_presuspend,
|
||||||
|
.postsuspend = raid_postsuspend,
|
||||||
|
.resume = raid_resume,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init dm_raid_init(void)
|
||||||
|
{
|
||||||
|
return dm_register_target(&raid_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit dm_raid_exit(void)
|
||||||
|
{
|
||||||
|
dm_unregister_target(&raid_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(dm_raid_init);
|
||||||
|
module_exit(dm_raid_exit);
|
||||||
|
|
||||||
|
MODULE_DESCRIPTION(DM_NAME " raid4/5/6 target");
|
||||||
|
MODULE_ALIAS("dm-raid4");
|
||||||
|
MODULE_ALIAS("dm-raid5");
|
||||||
|
MODULE_ALIAS("dm-raid6");
|
||||||
|
MODULE_AUTHOR("Neil Brown <dm-devel@redhat.com>");
|
||||||
|
MODULE_LICENSE("GPL");
|
|
@ -261,7 +261,7 @@ static int mirror_flush(struct dm_target *ti)
|
||||||
struct dm_io_request io_req = {
|
struct dm_io_request io_req = {
|
||||||
.bi_rw = WRITE_FLUSH,
|
.bi_rw = WRITE_FLUSH,
|
||||||
.mem.type = DM_IO_KMEM,
|
.mem.type = DM_IO_KMEM,
|
||||||
.mem.ptr.bvec = NULL,
|
.mem.ptr.addr = NULL,
|
||||||
.client = ms->io_client,
|
.client = ms->io_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -637,6 +637,12 @@ static void do_write(struct mirror_set *ms, struct bio *bio)
|
||||||
.client = ms->io_client,
|
.client = ms->io_client,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (bio->bi_rw & REQ_DISCARD) {
|
||||||
|
io_req.bi_rw |= REQ_DISCARD;
|
||||||
|
io_req.mem.type = DM_IO_KMEM;
|
||||||
|
io_req.mem.ptr.addr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0, m = ms->mirror; i < ms->nr_mirrors; i++, m++)
|
for (i = 0, m = ms->mirror; i < ms->nr_mirrors; i++, m++)
|
||||||
map_region(dest++, m, bio);
|
map_region(dest++, m, bio);
|
||||||
|
|
||||||
|
@ -670,7 +676,8 @@ static void do_writes(struct mirror_set *ms, struct bio_list *writes)
|
||||||
bio_list_init(&requeue);
|
bio_list_init(&requeue);
|
||||||
|
|
||||||
while ((bio = bio_list_pop(writes))) {
|
while ((bio = bio_list_pop(writes))) {
|
||||||
if (bio->bi_rw & REQ_FLUSH) {
|
if ((bio->bi_rw & REQ_FLUSH) ||
|
||||||
|
(bio->bi_rw & REQ_DISCARD)) {
|
||||||
bio_list_add(&sync, bio);
|
bio_list_add(&sync, bio);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -1076,8 +1083,10 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
ti->private = ms;
|
ti->private = ms;
|
||||||
ti->split_io = dm_rh_get_region_size(ms->rh);
|
ti->split_io = dm_rh_get_region_size(ms->rh);
|
||||||
ti->num_flush_requests = 1;
|
ti->num_flush_requests = 1;
|
||||||
|
ti->num_discard_requests = 1;
|
||||||
|
|
||||||
ms->kmirrord_wq = create_singlethread_workqueue("kmirrord");
|
ms->kmirrord_wq = alloc_workqueue("kmirrord",
|
||||||
|
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
|
||||||
if (!ms->kmirrord_wq) {
|
if (!ms->kmirrord_wq) {
|
||||||
DMERR("couldn't start kmirrord");
|
DMERR("couldn't start kmirrord");
|
||||||
r = -ENOMEM;
|
r = -ENOMEM;
|
||||||
|
@ -1130,7 +1139,7 @@ static void mirror_dtr(struct dm_target *ti)
|
||||||
|
|
||||||
del_timer_sync(&ms->timer);
|
del_timer_sync(&ms->timer);
|
||||||
flush_workqueue(ms->kmirrord_wq);
|
flush_workqueue(ms->kmirrord_wq);
|
||||||
flush_scheduled_work();
|
flush_work_sync(&ms->trigger_event);
|
||||||
dm_kcopyd_client_destroy(ms->kcopyd_client);
|
dm_kcopyd_client_destroy(ms->kcopyd_client);
|
||||||
destroy_workqueue(ms->kmirrord_wq);
|
destroy_workqueue(ms->kmirrord_wq);
|
||||||
free_context(ms, ti, ms->nr_mirrors);
|
free_context(ms, ti, ms->nr_mirrors);
|
||||||
|
@ -1406,7 +1415,7 @@ static int mirror_iterate_devices(struct dm_target *ti,
|
||||||
|
|
||||||
static struct target_type mirror_target = {
|
static struct target_type mirror_target = {
|
||||||
.name = "mirror",
|
.name = "mirror",
|
||||||
.version = {1, 12, 0},
|
.version = {1, 12, 1},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = mirror_ctr,
|
.ctr = mirror_ctr,
|
||||||
.dtr = mirror_dtr,
|
.dtr = mirror_dtr,
|
||||||
|
|
|
@ -256,7 +256,7 @@ static int chunk_io(struct pstore *ps, void *area, chunk_t chunk, int rw,
|
||||||
*/
|
*/
|
||||||
INIT_WORK_ONSTACK(&req.work, do_metadata);
|
INIT_WORK_ONSTACK(&req.work, do_metadata);
|
||||||
queue_work(ps->metadata_wq, &req.work);
|
queue_work(ps->metadata_wq, &req.work);
|
||||||
flush_workqueue(ps->metadata_wq);
|
flush_work(&req.work);
|
||||||
|
|
||||||
return req.result;
|
return req.result;
|
||||||
}
|
}
|
||||||
|
@ -818,7 +818,7 @@ static int persistent_ctr(struct dm_exception_store *store,
|
||||||
atomic_set(&ps->pending_count, 0);
|
atomic_set(&ps->pending_count, 0);
|
||||||
ps->callbacks = NULL;
|
ps->callbacks = NULL;
|
||||||
|
|
||||||
ps->metadata_wq = create_singlethread_workqueue("ksnaphd");
|
ps->metadata_wq = alloc_workqueue("ksnaphd", WQ_MEM_RECLAIM, 0);
|
||||||
if (!ps->metadata_wq) {
|
if (!ps->metadata_wq) {
|
||||||
kfree(ps);
|
kfree(ps);
|
||||||
DMERR("couldn't start header metadata update thread");
|
DMERR("couldn't start header metadata update thread");
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
#include <linux/vmalloc.h>
|
#include <linux/vmalloc.h>
|
||||||
#include <linux/log2.h>
|
#include <linux/log2.h>
|
||||||
#include <linux/dm-kcopyd.h>
|
#include <linux/dm-kcopyd.h>
|
||||||
#include <linux/workqueue.h>
|
|
||||||
|
|
||||||
#include "dm-exception-store.h"
|
#include "dm-exception-store.h"
|
||||||
|
|
||||||
|
@ -80,9 +79,6 @@ struct dm_snapshot {
|
||||||
/* Origin writes don't trigger exceptions until this is set */
|
/* Origin writes don't trigger exceptions until this is set */
|
||||||
int active;
|
int active;
|
||||||
|
|
||||||
/* Whether or not owning mapped_device is suspended */
|
|
||||||
int suspended;
|
|
||||||
|
|
||||||
atomic_t pending_exceptions_count;
|
atomic_t pending_exceptions_count;
|
||||||
|
|
||||||
mempool_t *pending_pool;
|
mempool_t *pending_pool;
|
||||||
|
@ -106,10 +102,6 @@ struct dm_snapshot {
|
||||||
|
|
||||||
struct dm_kcopyd_client *kcopyd_client;
|
struct dm_kcopyd_client *kcopyd_client;
|
||||||
|
|
||||||
/* Queue of snapshot writes for ksnapd to flush */
|
|
||||||
struct bio_list queued_bios;
|
|
||||||
struct work_struct queued_bios_work;
|
|
||||||
|
|
||||||
/* Wait for events based on state_bits */
|
/* Wait for events based on state_bits */
|
||||||
unsigned long state_bits;
|
unsigned long state_bits;
|
||||||
|
|
||||||
|
@ -160,9 +152,6 @@ struct dm_dev *dm_snap_cow(struct dm_snapshot *s)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dm_snap_cow);
|
EXPORT_SYMBOL(dm_snap_cow);
|
||||||
|
|
||||||
static struct workqueue_struct *ksnapd;
|
|
||||||
static void flush_queued_bios(struct work_struct *work);
|
|
||||||
|
|
||||||
static sector_t chunk_to_sector(struct dm_exception_store *store,
|
static sector_t chunk_to_sector(struct dm_exception_store *store,
|
||||||
chunk_t chunk)
|
chunk_t chunk)
|
||||||
{
|
{
|
||||||
|
@ -1110,7 +1099,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
s->ti = ti;
|
s->ti = ti;
|
||||||
s->valid = 1;
|
s->valid = 1;
|
||||||
s->active = 0;
|
s->active = 0;
|
||||||
s->suspended = 0;
|
|
||||||
atomic_set(&s->pending_exceptions_count, 0);
|
atomic_set(&s->pending_exceptions_count, 0);
|
||||||
init_rwsem(&s->lock);
|
init_rwsem(&s->lock);
|
||||||
INIT_LIST_HEAD(&s->list);
|
INIT_LIST_HEAD(&s->list);
|
||||||
|
@ -1153,9 +1141,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
|
|
||||||
spin_lock_init(&s->tracked_chunk_lock);
|
spin_lock_init(&s->tracked_chunk_lock);
|
||||||
|
|
||||||
bio_list_init(&s->queued_bios);
|
|
||||||
INIT_WORK(&s->queued_bios_work, flush_queued_bios);
|
|
||||||
|
|
||||||
ti->private = s;
|
ti->private = s;
|
||||||
ti->num_flush_requests = num_flush_requests;
|
ti->num_flush_requests = num_flush_requests;
|
||||||
|
|
||||||
|
@ -1279,8 +1264,6 @@ static void snapshot_dtr(struct dm_target *ti)
|
||||||
struct dm_snapshot *s = ti->private;
|
struct dm_snapshot *s = ti->private;
|
||||||
struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
|
struct dm_snapshot *snap_src = NULL, *snap_dest = NULL;
|
||||||
|
|
||||||
flush_workqueue(ksnapd);
|
|
||||||
|
|
||||||
down_read(&_origins_lock);
|
down_read(&_origins_lock);
|
||||||
/* Check whether exception handover must be cancelled */
|
/* Check whether exception handover must be cancelled */
|
||||||
(void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
|
(void) __find_snapshots_sharing_cow(s, &snap_src, &snap_dest, NULL);
|
||||||
|
@ -1342,20 +1325,6 @@ static void flush_bios(struct bio *bio)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void flush_queued_bios(struct work_struct *work)
|
|
||||||
{
|
|
||||||
struct dm_snapshot *s =
|
|
||||||
container_of(work, struct dm_snapshot, queued_bios_work);
|
|
||||||
struct bio *queued_bios;
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
spin_lock_irqsave(&s->pe_lock, flags);
|
|
||||||
queued_bios = bio_list_get(&s->queued_bios);
|
|
||||||
spin_unlock_irqrestore(&s->pe_lock, flags);
|
|
||||||
|
|
||||||
flush_bios(queued_bios);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_origin(struct dm_dev *origin, struct bio *bio);
|
static int do_origin(struct dm_dev *origin, struct bio *bio);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1760,15 +1729,6 @@ static void snapshot_merge_presuspend(struct dm_target *ti)
|
||||||
stop_merge(s);
|
stop_merge(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void snapshot_postsuspend(struct dm_target *ti)
|
|
||||||
{
|
|
||||||
struct dm_snapshot *s = ti->private;
|
|
||||||
|
|
||||||
down_write(&s->lock);
|
|
||||||
s->suspended = 1;
|
|
||||||
up_write(&s->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int snapshot_preresume(struct dm_target *ti)
|
static int snapshot_preresume(struct dm_target *ti)
|
||||||
{
|
{
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
@ -1783,7 +1743,7 @@ static int snapshot_preresume(struct dm_target *ti)
|
||||||
DMERR("Unable to resume snapshot source until "
|
DMERR("Unable to resume snapshot source until "
|
||||||
"handover completes.");
|
"handover completes.");
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
} else if (!snap_src->suspended) {
|
} else if (!dm_suspended(snap_src->ti)) {
|
||||||
DMERR("Unable to perform snapshot handover until "
|
DMERR("Unable to perform snapshot handover until "
|
||||||
"source is suspended.");
|
"source is suspended.");
|
||||||
r = -EINVAL;
|
r = -EINVAL;
|
||||||
|
@ -1816,7 +1776,6 @@ static void snapshot_resume(struct dm_target *ti)
|
||||||
|
|
||||||
down_write(&s->lock);
|
down_write(&s->lock);
|
||||||
s->active = 1;
|
s->active = 1;
|
||||||
s->suspended = 0;
|
|
||||||
up_write(&s->lock);
|
up_write(&s->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2194,7 +2153,7 @@ static int origin_iterate_devices(struct dm_target *ti,
|
||||||
|
|
||||||
static struct target_type origin_target = {
|
static struct target_type origin_target = {
|
||||||
.name = "snapshot-origin",
|
.name = "snapshot-origin",
|
||||||
.version = {1, 7, 0},
|
.version = {1, 7, 1},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = origin_ctr,
|
.ctr = origin_ctr,
|
||||||
.dtr = origin_dtr,
|
.dtr = origin_dtr,
|
||||||
|
@ -2207,13 +2166,12 @@ static struct target_type origin_target = {
|
||||||
|
|
||||||
static struct target_type snapshot_target = {
|
static struct target_type snapshot_target = {
|
||||||
.name = "snapshot",
|
.name = "snapshot",
|
||||||
.version = {1, 9, 0},
|
.version = {1, 10, 0},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = snapshot_ctr,
|
.ctr = snapshot_ctr,
|
||||||
.dtr = snapshot_dtr,
|
.dtr = snapshot_dtr,
|
||||||
.map = snapshot_map,
|
.map = snapshot_map,
|
||||||
.end_io = snapshot_end_io,
|
.end_io = snapshot_end_io,
|
||||||
.postsuspend = snapshot_postsuspend,
|
|
||||||
.preresume = snapshot_preresume,
|
.preresume = snapshot_preresume,
|
||||||
.resume = snapshot_resume,
|
.resume = snapshot_resume,
|
||||||
.status = snapshot_status,
|
.status = snapshot_status,
|
||||||
|
@ -2222,14 +2180,13 @@ static struct target_type snapshot_target = {
|
||||||
|
|
||||||
static struct target_type merge_target = {
|
static struct target_type merge_target = {
|
||||||
.name = dm_snapshot_merge_target_name,
|
.name = dm_snapshot_merge_target_name,
|
||||||
.version = {1, 0, 0},
|
.version = {1, 1, 0},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = snapshot_ctr,
|
.ctr = snapshot_ctr,
|
||||||
.dtr = snapshot_dtr,
|
.dtr = snapshot_dtr,
|
||||||
.map = snapshot_merge_map,
|
.map = snapshot_merge_map,
|
||||||
.end_io = snapshot_end_io,
|
.end_io = snapshot_end_io,
|
||||||
.presuspend = snapshot_merge_presuspend,
|
.presuspend = snapshot_merge_presuspend,
|
||||||
.postsuspend = snapshot_postsuspend,
|
|
||||||
.preresume = snapshot_preresume,
|
.preresume = snapshot_preresume,
|
||||||
.resume = snapshot_merge_resume,
|
.resume = snapshot_merge_resume,
|
||||||
.status = snapshot_status,
|
.status = snapshot_status,
|
||||||
|
@ -2291,17 +2248,8 @@ static int __init dm_snapshot_init(void)
|
||||||
goto bad_tracked_chunk_cache;
|
goto bad_tracked_chunk_cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksnapd = create_singlethread_workqueue("ksnapd");
|
|
||||||
if (!ksnapd) {
|
|
||||||
DMERR("Failed to create ksnapd workqueue.");
|
|
||||||
r = -ENOMEM;
|
|
||||||
goto bad_pending_pool;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
bad_pending_pool:
|
|
||||||
kmem_cache_destroy(tracked_chunk_cache);
|
|
||||||
bad_tracked_chunk_cache:
|
bad_tracked_chunk_cache:
|
||||||
kmem_cache_destroy(pending_cache);
|
kmem_cache_destroy(pending_cache);
|
||||||
bad_pending_cache:
|
bad_pending_cache:
|
||||||
|
@ -2322,8 +2270,6 @@ bad_register_snapshot_target:
|
||||||
|
|
||||||
static void __exit dm_snapshot_exit(void)
|
static void __exit dm_snapshot_exit(void)
|
||||||
{
|
{
|
||||||
destroy_workqueue(ksnapd);
|
|
||||||
|
|
||||||
dm_unregister_target(&snapshot_target);
|
dm_unregister_target(&snapshot_target);
|
||||||
dm_unregister_target(&origin_target);
|
dm_unregister_target(&origin_target);
|
||||||
dm_unregister_target(&merge_target);
|
dm_unregister_target(&merge_target);
|
||||||
|
|
|
@ -39,23 +39,20 @@ struct stripe_c {
|
||||||
struct dm_target *ti;
|
struct dm_target *ti;
|
||||||
|
|
||||||
/* Work struct used for triggering events*/
|
/* Work struct used for triggering events*/
|
||||||
struct work_struct kstriped_ws;
|
struct work_struct trigger_event;
|
||||||
|
|
||||||
struct stripe stripe[0];
|
struct stripe stripe[0];
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct workqueue_struct *kstriped;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* An event is triggered whenever a drive
|
* An event is triggered whenever a drive
|
||||||
* drops out of a stripe volume.
|
* drops out of a stripe volume.
|
||||||
*/
|
*/
|
||||||
static void trigger_event(struct work_struct *work)
|
static void trigger_event(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct stripe_c *sc = container_of(work, struct stripe_c, kstriped_ws);
|
struct stripe_c *sc = container_of(work, struct stripe_c,
|
||||||
|
trigger_event);
|
||||||
dm_table_event(sc->ti->table);
|
dm_table_event(sc->ti->table);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct stripe_c *alloc_context(unsigned int stripes)
|
static inline struct stripe_c *alloc_context(unsigned int stripes)
|
||||||
|
@ -160,7 +157,7 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&sc->kstriped_ws, trigger_event);
|
INIT_WORK(&sc->trigger_event, trigger_event);
|
||||||
|
|
||||||
/* Set pointer to dm target; used in trigger_event */
|
/* Set pointer to dm target; used in trigger_event */
|
||||||
sc->ti = ti;
|
sc->ti = ti;
|
||||||
|
@ -211,7 +208,7 @@ static void stripe_dtr(struct dm_target *ti)
|
||||||
for (i = 0; i < sc->stripes; i++)
|
for (i = 0; i < sc->stripes; i++)
|
||||||
dm_put_device(ti, sc->stripe[i].dev);
|
dm_put_device(ti, sc->stripe[i].dev);
|
||||||
|
|
||||||
flush_workqueue(kstriped);
|
flush_work_sync(&sc->trigger_event);
|
||||||
kfree(sc);
|
kfree(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -367,7 +364,7 @@ static int stripe_end_io(struct dm_target *ti, struct bio *bio,
|
||||||
atomic_inc(&(sc->stripe[i].error_count));
|
atomic_inc(&(sc->stripe[i].error_count));
|
||||||
if (atomic_read(&(sc->stripe[i].error_count)) <
|
if (atomic_read(&(sc->stripe[i].error_count)) <
|
||||||
DM_IO_ERROR_THRESHOLD)
|
DM_IO_ERROR_THRESHOLD)
|
||||||
queue_work(kstriped, &sc->kstriped_ws);
|
schedule_work(&sc->trigger_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
|
@ -401,7 +398,7 @@ static void stripe_io_hints(struct dm_target *ti,
|
||||||
|
|
||||||
static struct target_type stripe_target = {
|
static struct target_type stripe_target = {
|
||||||
.name = "striped",
|
.name = "striped",
|
||||||
.version = {1, 3, 0},
|
.version = {1, 3, 1},
|
||||||
.module = THIS_MODULE,
|
.module = THIS_MODULE,
|
||||||
.ctr = stripe_ctr,
|
.ctr = stripe_ctr,
|
||||||
.dtr = stripe_dtr,
|
.dtr = stripe_dtr,
|
||||||
|
@ -422,20 +419,10 @@ int __init dm_stripe_init(void)
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
kstriped = create_singlethread_workqueue("kstriped");
|
|
||||||
if (!kstriped) {
|
|
||||||
DMERR("failed to create workqueue kstriped");
|
|
||||||
dm_unregister_target(&stripe_target);
|
|
||||||
return -ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
void dm_stripe_exit(void)
|
void dm_stripe_exit(void)
|
||||||
{
|
{
|
||||||
dm_unregister_target(&stripe_target);
|
dm_unregister_target(&stripe_target);
|
||||||
destroy_workqueue(kstriped);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,6 +71,8 @@ struct dm_table {
|
||||||
void *event_context;
|
void *event_context;
|
||||||
|
|
||||||
struct dm_md_mempools *mempools;
|
struct dm_md_mempools *mempools;
|
||||||
|
|
||||||
|
struct list_head target_callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -204,6 +206,7 @@ int dm_table_create(struct dm_table **result, fmode_t mode,
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&t->devices);
|
INIT_LIST_HEAD(&t->devices);
|
||||||
|
INIT_LIST_HEAD(&t->target_callbacks);
|
||||||
atomic_set(&t->holders, 0);
|
atomic_set(&t->holders, 0);
|
||||||
t->discards_supported = 1;
|
t->discards_supported = 1;
|
||||||
|
|
||||||
|
@ -1225,10 +1228,17 @@ int dm_table_resume_targets(struct dm_table *t)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dm_table_add_target_callbacks(struct dm_table *t, struct dm_target_callbacks *cb)
|
||||||
|
{
|
||||||
|
list_add(&cb->list, &t->target_callbacks);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(dm_table_add_target_callbacks);
|
||||||
|
|
||||||
int dm_table_any_congested(struct dm_table *t, int bdi_bits)
|
int dm_table_any_congested(struct dm_table *t, int bdi_bits)
|
||||||
{
|
{
|
||||||
struct dm_dev_internal *dd;
|
struct dm_dev_internal *dd;
|
||||||
struct list_head *devices = dm_table_get_devices(t);
|
struct list_head *devices = dm_table_get_devices(t);
|
||||||
|
struct dm_target_callbacks *cb;
|
||||||
int r = 0;
|
int r = 0;
|
||||||
|
|
||||||
list_for_each_entry(dd, devices, list) {
|
list_for_each_entry(dd, devices, list) {
|
||||||
|
@ -1243,6 +1253,10 @@ int dm_table_any_congested(struct dm_table *t, int bdi_bits)
|
||||||
bdevname(dd->dm_dev.bdev, b));
|
bdevname(dd->dm_dev.bdev, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(cb, &t->target_callbacks, list)
|
||||||
|
if (cb->congested_fn)
|
||||||
|
r |= cb->congested_fn(cb, bdi_bits);
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,6 +1278,7 @@ void dm_table_unplug_all(struct dm_table *t)
|
||||||
{
|
{
|
||||||
struct dm_dev_internal *dd;
|
struct dm_dev_internal *dd;
|
||||||
struct list_head *devices = dm_table_get_devices(t);
|
struct list_head *devices = dm_table_get_devices(t);
|
||||||
|
struct dm_target_callbacks *cb;
|
||||||
|
|
||||||
list_for_each_entry(dd, devices, list) {
|
list_for_each_entry(dd, devices, list) {
|
||||||
struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev);
|
struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev);
|
||||||
|
@ -1276,6 +1291,10 @@ void dm_table_unplug_all(struct dm_table *t)
|
||||||
dm_device_name(t->md),
|
dm_device_name(t->md),
|
||||||
bdevname(dd->dm_dev.bdev, b));
|
bdevname(dd->dm_dev.bdev, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(cb, &t->target_callbacks, list)
|
||||||
|
if (cb->unplug_fn)
|
||||||
|
cb->unplug_fn(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct mapped_device *dm_table_get_md(struct dm_table *t)
|
struct mapped_device *dm_table_get_md(struct dm_table *t)
|
||||||
|
|
|
@ -32,7 +32,6 @@
|
||||||
#define DM_COOKIE_ENV_VAR_NAME "DM_COOKIE"
|
#define DM_COOKIE_ENV_VAR_NAME "DM_COOKIE"
|
||||||
#define DM_COOKIE_LENGTH 24
|
#define DM_COOKIE_LENGTH 24
|
||||||
|
|
||||||
static DEFINE_MUTEX(dm_mutex);
|
|
||||||
static const char *_name = DM_NAME;
|
static const char *_name = DM_NAME;
|
||||||
|
|
||||||
static unsigned int major = 0;
|
static unsigned int major = 0;
|
||||||
|
@ -328,7 +327,6 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
|
||||||
{
|
{
|
||||||
struct mapped_device *md;
|
struct mapped_device *md;
|
||||||
|
|
||||||
mutex_lock(&dm_mutex);
|
|
||||||
spin_lock(&_minor_lock);
|
spin_lock(&_minor_lock);
|
||||||
|
|
||||||
md = bdev->bd_disk->private_data;
|
md = bdev->bd_disk->private_data;
|
||||||
|
@ -346,7 +344,6 @@ static int dm_blk_open(struct block_device *bdev, fmode_t mode)
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock(&_minor_lock);
|
spin_unlock(&_minor_lock);
|
||||||
mutex_unlock(&dm_mutex);
|
|
||||||
|
|
||||||
return md ? 0 : -ENXIO;
|
return md ? 0 : -ENXIO;
|
||||||
}
|
}
|
||||||
|
@ -355,10 +352,12 @@ static int dm_blk_close(struct gendisk *disk, fmode_t mode)
|
||||||
{
|
{
|
||||||
struct mapped_device *md = disk->private_data;
|
struct mapped_device *md = disk->private_data;
|
||||||
|
|
||||||
mutex_lock(&dm_mutex);
|
spin_lock(&_minor_lock);
|
||||||
|
|
||||||
atomic_dec(&md->open_count);
|
atomic_dec(&md->open_count);
|
||||||
dm_put(md);
|
dm_put(md);
|
||||||
mutex_unlock(&dm_mutex);
|
|
||||||
|
spin_unlock(&_minor_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1638,13 +1637,15 @@ static void dm_request_fn(struct request_queue *q)
|
||||||
if (map_request(ti, clone, md))
|
if (map_request(ti, clone, md))
|
||||||
goto requeued;
|
goto requeued;
|
||||||
|
|
||||||
spin_lock_irq(q->queue_lock);
|
BUG_ON(!irqs_disabled());
|
||||||
|
spin_lock(q->queue_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
requeued:
|
requeued:
|
||||||
spin_lock_irq(q->queue_lock);
|
BUG_ON(!irqs_disabled());
|
||||||
|
spin_lock(q->queue_lock);
|
||||||
|
|
||||||
plug_and_out:
|
plug_and_out:
|
||||||
if (!elv_queue_empty(q))
|
if (!elv_queue_empty(q))
|
||||||
|
@ -1884,7 +1885,8 @@ static struct mapped_device *alloc_dev(int minor)
|
||||||
add_disk(md->disk);
|
add_disk(md->disk);
|
||||||
format_dev_t(md->name, MKDEV(_major, minor));
|
format_dev_t(md->name, MKDEV(_major, minor));
|
||||||
|
|
||||||
md->wq = create_singlethread_workqueue("kdmflush");
|
md->wq = alloc_workqueue("kdmflush",
|
||||||
|
WQ_NON_REENTRANT | WQ_MEM_RECLAIM, 0);
|
||||||
if (!md->wq)
|
if (!md->wq)
|
||||||
goto bad_thread;
|
goto bad_thread;
|
||||||
|
|
||||||
|
@ -1992,13 +1994,14 @@ static void event_callback(void *context)
|
||||||
wake_up(&md->eventq);
|
wake_up(&md->eventq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Protected by md->suspend_lock obtained by dm_swap_table().
|
||||||
|
*/
|
||||||
static void __set_size(struct mapped_device *md, sector_t size)
|
static void __set_size(struct mapped_device *md, sector_t size)
|
||||||
{
|
{
|
||||||
set_capacity(md->disk, size);
|
set_capacity(md->disk, size);
|
||||||
|
|
||||||
mutex_lock(&md->bdev->bd_inode->i_mutex);
|
|
||||||
i_size_write(md->bdev->bd_inode, (loff_t)size << SECTOR_SHIFT);
|
i_size_write(md->bdev->bd_inode, (loff_t)size << SECTOR_SHIFT);
|
||||||
mutex_unlock(&md->bdev->bd_inode->i_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
197
drivers/md/md.c
197
drivers/md/md.c
|
@ -288,10 +288,12 @@ static int md_make_request(struct request_queue *q, struct bio *bio)
|
||||||
int rv;
|
int rv;
|
||||||
int cpu;
|
int cpu;
|
||||||
|
|
||||||
if (mddev == NULL || mddev->pers == NULL) {
|
if (mddev == NULL || mddev->pers == NULL
|
||||||
|
|| !mddev->ready) {
|
||||||
bio_io_error(bio);
|
bio_io_error(bio);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
smp_rmb(); /* Ensure implications of 'active' are visible */
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (mddev->suspended) {
|
if (mddev->suspended) {
|
||||||
DEFINE_WAIT(__wait);
|
DEFINE_WAIT(__wait);
|
||||||
|
@ -703,9 +705,9 @@ static struct mdk_personality *find_pers(int level, char *clevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return the offset of the super block in 512byte sectors */
|
/* return the offset of the super block in 512byte sectors */
|
||||||
static inline sector_t calc_dev_sboffset(struct block_device *bdev)
|
static inline sector_t calc_dev_sboffset(mdk_rdev_t *rdev)
|
||||||
{
|
{
|
||||||
sector_t num_sectors = i_size_read(bdev->bd_inode) / 512;
|
sector_t num_sectors = i_size_read(rdev->bdev->bd_inode) / 512;
|
||||||
return MD_NEW_SIZE_SECTORS(num_sectors);
|
return MD_NEW_SIZE_SECTORS(num_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -763,7 +765,7 @@ void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
|
||||||
*/
|
*/
|
||||||
struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, mddev);
|
struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, mddev);
|
||||||
|
|
||||||
bio->bi_bdev = rdev->bdev;
|
bio->bi_bdev = rdev->meta_bdev ? rdev->meta_bdev : rdev->bdev;
|
||||||
bio->bi_sector = sector;
|
bio->bi_sector = sector;
|
||||||
bio_add_page(bio, page, size, 0);
|
bio_add_page(bio, page, size, 0);
|
||||||
bio->bi_private = rdev;
|
bio->bi_private = rdev;
|
||||||
|
@ -793,7 +795,7 @@ static void bi_complete(struct bio *bio, int error)
|
||||||
}
|
}
|
||||||
|
|
||||||
int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
|
int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
|
||||||
struct page *page, int rw)
|
struct page *page, int rw, bool metadata_op)
|
||||||
{
|
{
|
||||||
struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, rdev->mddev);
|
struct bio *bio = bio_alloc_mddev(GFP_NOIO, 1, rdev->mddev);
|
||||||
struct completion event;
|
struct completion event;
|
||||||
|
@ -801,8 +803,12 @@ int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
|
||||||
|
|
||||||
rw |= REQ_SYNC | REQ_UNPLUG;
|
rw |= REQ_SYNC | REQ_UNPLUG;
|
||||||
|
|
||||||
bio->bi_bdev = rdev->bdev;
|
bio->bi_bdev = (metadata_op && rdev->meta_bdev) ?
|
||||||
bio->bi_sector = sector;
|
rdev->meta_bdev : rdev->bdev;
|
||||||
|
if (metadata_op)
|
||||||
|
bio->bi_sector = sector + rdev->sb_start;
|
||||||
|
else
|
||||||
|
bio->bi_sector = sector + rdev->data_offset;
|
||||||
bio_add_page(bio, page, size, 0);
|
bio_add_page(bio, page, size, 0);
|
||||||
init_completion(&event);
|
init_completion(&event);
|
||||||
bio->bi_private = &event;
|
bio->bi_private = &event;
|
||||||
|
@ -827,7 +833,7 @@ static int read_disk_sb(mdk_rdev_t * rdev, int size)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
if (!sync_page_io(rdev, rdev->sb_start, size, rdev->sb_page, READ))
|
if (!sync_page_io(rdev, 0, size, rdev->sb_page, READ, true))
|
||||||
goto fail;
|
goto fail;
|
||||||
rdev->sb_loaded = 1;
|
rdev->sb_loaded = 1;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -989,7 +995,7 @@ static int super_90_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version
|
||||||
*
|
*
|
||||||
* It also happens to be a multiple of 4Kb.
|
* It also happens to be a multiple of 4Kb.
|
||||||
*/
|
*/
|
||||||
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
rdev->sb_start = calc_dev_sboffset(rdev);
|
||||||
|
|
||||||
ret = read_disk_sb(rdev, MD_SB_BYTES);
|
ret = read_disk_sb(rdev, MD_SB_BYTES);
|
||||||
if (ret) return ret;
|
if (ret) return ret;
|
||||||
|
@ -1330,7 +1336,7 @@ super_90_rdev_size_change(mdk_rdev_t *rdev, sector_t num_sectors)
|
||||||
return 0; /* component must fit device */
|
return 0; /* component must fit device */
|
||||||
if (rdev->mddev->bitmap_info.offset)
|
if (rdev->mddev->bitmap_info.offset)
|
||||||
return 0; /* can't move bitmap */
|
return 0; /* can't move bitmap */
|
||||||
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
rdev->sb_start = calc_dev_sboffset(rdev);
|
||||||
if (!num_sectors || num_sectors > rdev->sb_start)
|
if (!num_sectors || num_sectors > rdev->sb_start)
|
||||||
num_sectors = rdev->sb_start;
|
num_sectors = rdev->sb_start;
|
||||||
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
|
md_super_write(rdev->mddev, rdev, rdev->sb_start, rdev->sb_size,
|
||||||
|
@ -2465,6 +2471,10 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
||||||
if (rdev2->raid_disk == slot)
|
if (rdev2->raid_disk == slot)
|
||||||
return -EEXIST;
|
return -EEXIST;
|
||||||
|
|
||||||
|
if (slot >= rdev->mddev->raid_disks &&
|
||||||
|
slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
|
||||||
|
return -ENOSPC;
|
||||||
|
|
||||||
rdev->raid_disk = slot;
|
rdev->raid_disk = slot;
|
||||||
if (test_bit(In_sync, &rdev->flags))
|
if (test_bit(In_sync, &rdev->flags))
|
||||||
rdev->saved_raid_disk = slot;
|
rdev->saved_raid_disk = slot;
|
||||||
|
@ -2482,7 +2492,8 @@ slot_store(mdk_rdev_t *rdev, const char *buf, size_t len)
|
||||||
/* failure here is OK */;
|
/* failure here is OK */;
|
||||||
/* don't wakeup anyone, leave that to userspace. */
|
/* don't wakeup anyone, leave that to userspace. */
|
||||||
} else {
|
} else {
|
||||||
if (slot >= rdev->mddev->raid_disks)
|
if (slot >= rdev->mddev->raid_disks &&
|
||||||
|
slot >= rdev->mddev->raid_disks + rdev->mddev->delta_disks)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
rdev->raid_disk = slot;
|
rdev->raid_disk = slot;
|
||||||
/* assume it is working */
|
/* assume it is working */
|
||||||
|
@ -3107,7 +3118,7 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
|
||||||
char nm[20];
|
char nm[20];
|
||||||
if (rdev->raid_disk < 0)
|
if (rdev->raid_disk < 0)
|
||||||
continue;
|
continue;
|
||||||
if (rdev->new_raid_disk > mddev->raid_disks)
|
if (rdev->new_raid_disk >= mddev->raid_disks)
|
||||||
rdev->new_raid_disk = -1;
|
rdev->new_raid_disk = -1;
|
||||||
if (rdev->new_raid_disk == rdev->raid_disk)
|
if (rdev->new_raid_disk == rdev->raid_disk)
|
||||||
continue;
|
continue;
|
||||||
|
@ -3736,6 +3747,8 @@ action_show(mddev_t *mddev, char *page)
|
||||||
return sprintf(page, "%s\n", type);
|
return sprintf(page, "%s\n", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reap_sync_thread(mddev_t *mddev);
|
||||||
|
|
||||||
static ssize_t
|
static ssize_t
|
||||||
action_store(mddev_t *mddev, const char *page, size_t len)
|
action_store(mddev_t *mddev, const char *page, size_t len)
|
||||||
{
|
{
|
||||||
|
@ -3750,9 +3763,7 @@ action_store(mddev_t *mddev, const char *page, size_t len)
|
||||||
if (cmd_match(page, "idle") || cmd_match(page, "frozen")) {
|
if (cmd_match(page, "idle") || cmd_match(page, "frozen")) {
|
||||||
if (mddev->sync_thread) {
|
if (mddev->sync_thread) {
|
||||||
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
||||||
md_unregister_thread(mddev->sync_thread);
|
reap_sync_thread(mddev);
|
||||||
mddev->sync_thread = NULL;
|
|
||||||
mddev->recovery = 0;
|
|
||||||
}
|
}
|
||||||
} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
|
} else if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) ||
|
||||||
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
|
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery))
|
||||||
|
@ -3904,7 +3915,7 @@ static struct md_sysfs_entry md_sync_speed = __ATTR_RO(sync_speed);
|
||||||
static ssize_t
|
static ssize_t
|
||||||
sync_completed_show(mddev_t *mddev, char *page)
|
sync_completed_show(mddev_t *mddev, char *page)
|
||||||
{
|
{
|
||||||
unsigned long max_sectors, resync;
|
unsigned long long max_sectors, resync;
|
||||||
|
|
||||||
if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
|
||||||
return sprintf(page, "none\n");
|
return sprintf(page, "none\n");
|
||||||
|
@ -3915,7 +3926,7 @@ sync_completed_show(mddev_t *mddev, char *page)
|
||||||
max_sectors = mddev->dev_sectors;
|
max_sectors = mddev->dev_sectors;
|
||||||
|
|
||||||
resync = mddev->curr_resync_completed;
|
resync = mddev->curr_resync_completed;
|
||||||
return sprintf(page, "%lu / %lu\n", resync, max_sectors);
|
return sprintf(page, "%llu / %llu\n", resync, max_sectors);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
|
static struct md_sysfs_entry md_sync_completed = __ATTR_RO(sync_completed);
|
||||||
|
@ -4002,19 +4013,24 @@ suspend_lo_store(mddev_t *mddev, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
char *e;
|
char *e;
|
||||||
unsigned long long new = simple_strtoull(buf, &e, 10);
|
unsigned long long new = simple_strtoull(buf, &e, 10);
|
||||||
|
unsigned long long old = mddev->suspend_lo;
|
||||||
|
|
||||||
if (mddev->pers == NULL ||
|
if (mddev->pers == NULL ||
|
||||||
mddev->pers->quiesce == NULL)
|
mddev->pers->quiesce == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (buf == e || (*e && *e != '\n'))
|
if (buf == e || (*e && *e != '\n'))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (new >= mddev->suspend_hi ||
|
|
||||||
(new > mddev->suspend_lo && new < mddev->suspend_hi)) {
|
mddev->suspend_lo = new;
|
||||||
mddev->suspend_lo = new;
|
if (new >= old)
|
||||||
|
/* Shrinking suspended region */
|
||||||
mddev->pers->quiesce(mddev, 2);
|
mddev->pers->quiesce(mddev, 2);
|
||||||
return len;
|
else {
|
||||||
} else
|
/* Expanding suspended region - need to wait */
|
||||||
return -EINVAL;
|
mddev->pers->quiesce(mddev, 1);
|
||||||
|
mddev->pers->quiesce(mddev, 0);
|
||||||
|
}
|
||||||
|
return len;
|
||||||
}
|
}
|
||||||
static struct md_sysfs_entry md_suspend_lo =
|
static struct md_sysfs_entry md_suspend_lo =
|
||||||
__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
|
__ATTR(suspend_lo, S_IRUGO|S_IWUSR, suspend_lo_show, suspend_lo_store);
|
||||||
|
@ -4031,20 +4047,24 @@ suspend_hi_store(mddev_t *mddev, const char *buf, size_t len)
|
||||||
{
|
{
|
||||||
char *e;
|
char *e;
|
||||||
unsigned long long new = simple_strtoull(buf, &e, 10);
|
unsigned long long new = simple_strtoull(buf, &e, 10);
|
||||||
|
unsigned long long old = mddev->suspend_hi;
|
||||||
|
|
||||||
if (mddev->pers == NULL ||
|
if (mddev->pers == NULL ||
|
||||||
mddev->pers->quiesce == NULL)
|
mddev->pers->quiesce == NULL)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if (buf == e || (*e && *e != '\n'))
|
if (buf == e || (*e && *e != '\n'))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
if ((new <= mddev->suspend_lo && mddev->suspend_lo >= mddev->suspend_hi) ||
|
|
||||||
(new > mddev->suspend_lo && new > mddev->suspend_hi)) {
|
mddev->suspend_hi = new;
|
||||||
mddev->suspend_hi = new;
|
if (new <= old)
|
||||||
|
/* Shrinking suspended region */
|
||||||
|
mddev->pers->quiesce(mddev, 2);
|
||||||
|
else {
|
||||||
|
/* Expanding suspended region - need to wait */
|
||||||
mddev->pers->quiesce(mddev, 1);
|
mddev->pers->quiesce(mddev, 1);
|
||||||
mddev->pers->quiesce(mddev, 0);
|
mddev->pers->quiesce(mddev, 0);
|
||||||
return len;
|
}
|
||||||
} else
|
return len;
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
static struct md_sysfs_entry md_suspend_hi =
|
static struct md_sysfs_entry md_suspend_hi =
|
||||||
__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
|
__ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
|
||||||
|
@ -4422,7 +4442,9 @@ int md_run(mddev_t *mddev)
|
||||||
* We don't want the data to overlap the metadata,
|
* We don't want the data to overlap the metadata,
|
||||||
* Internal Bitmap issues have been handled elsewhere.
|
* Internal Bitmap issues have been handled elsewhere.
|
||||||
*/
|
*/
|
||||||
if (rdev->data_offset < rdev->sb_start) {
|
if (rdev->meta_bdev) {
|
||||||
|
/* Nothing to check */;
|
||||||
|
} else if (rdev->data_offset < rdev->sb_start) {
|
||||||
if (mddev->dev_sectors &&
|
if (mddev->dev_sectors &&
|
||||||
rdev->data_offset + mddev->dev_sectors
|
rdev->data_offset + mddev->dev_sectors
|
||||||
> rdev->sb_start) {
|
> rdev->sb_start) {
|
||||||
|
@ -4556,7 +4578,8 @@ int md_run(mddev_t *mddev)
|
||||||
mddev->safemode_timer.data = (unsigned long) mddev;
|
mddev->safemode_timer.data = (unsigned long) mddev;
|
||||||
mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
|
mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
|
||||||
mddev->in_sync = 1;
|
mddev->in_sync = 1;
|
||||||
|
smp_wmb();
|
||||||
|
mddev->ready = 1;
|
||||||
list_for_each_entry(rdev, &mddev->disks, same_set)
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
||||||
if (rdev->raid_disk >= 0) {
|
if (rdev->raid_disk >= 0) {
|
||||||
char nm[20];
|
char nm[20];
|
||||||
|
@ -4693,13 +4716,12 @@ static void md_clean(mddev_t *mddev)
|
||||||
mddev->plug = NULL;
|
mddev->plug = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void md_stop_writes(mddev_t *mddev)
|
static void __md_stop_writes(mddev_t *mddev)
|
||||||
{
|
{
|
||||||
if (mddev->sync_thread) {
|
if (mddev->sync_thread) {
|
||||||
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
|
||||||
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
|
||||||
md_unregister_thread(mddev->sync_thread);
|
reap_sync_thread(mddev);
|
||||||
mddev->sync_thread = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
del_timer_sync(&mddev->safemode_timer);
|
del_timer_sync(&mddev->safemode_timer);
|
||||||
|
@ -4713,10 +4735,18 @@ void md_stop_writes(mddev_t *mddev)
|
||||||
md_update_sb(mddev, 1);
|
md_update_sb(mddev, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void md_stop_writes(mddev_t *mddev)
|
||||||
|
{
|
||||||
|
mddev_lock(mddev);
|
||||||
|
__md_stop_writes(mddev);
|
||||||
|
mddev_unlock(mddev);
|
||||||
|
}
|
||||||
EXPORT_SYMBOL_GPL(md_stop_writes);
|
EXPORT_SYMBOL_GPL(md_stop_writes);
|
||||||
|
|
||||||
void md_stop(mddev_t *mddev)
|
void md_stop(mddev_t *mddev)
|
||||||
{
|
{
|
||||||
|
mddev->ready = 0;
|
||||||
mddev->pers->stop(mddev);
|
mddev->pers->stop(mddev);
|
||||||
if (mddev->pers->sync_request && mddev->to_remove == NULL)
|
if (mddev->pers->sync_request && mddev->to_remove == NULL)
|
||||||
mddev->to_remove = &md_redundancy_group;
|
mddev->to_remove = &md_redundancy_group;
|
||||||
|
@ -4736,7 +4766,7 @@ static int md_set_readonly(mddev_t *mddev, int is_open)
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (mddev->pers) {
|
if (mddev->pers) {
|
||||||
md_stop_writes(mddev);
|
__md_stop_writes(mddev);
|
||||||
|
|
||||||
err = -ENXIO;
|
err = -ENXIO;
|
||||||
if (mddev->ro==1)
|
if (mddev->ro==1)
|
||||||
|
@ -4773,7 +4803,7 @@ static int do_md_stop(mddev_t * mddev, int mode, int is_open)
|
||||||
if (mddev->ro)
|
if (mddev->ro)
|
||||||
set_disk_ro(disk, 0);
|
set_disk_ro(disk, 0);
|
||||||
|
|
||||||
md_stop_writes(mddev);
|
__md_stop_writes(mddev);
|
||||||
md_stop(mddev);
|
md_stop(mddev);
|
||||||
mddev->queue->merge_bvec_fn = NULL;
|
mddev->queue->merge_bvec_fn = NULL;
|
||||||
mddev->queue->unplug_fn = NULL;
|
mddev->queue->unplug_fn = NULL;
|
||||||
|
@ -5151,9 +5181,10 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
|
||||||
/* set saved_raid_disk if appropriate */
|
/* set saved_raid_disk if appropriate */
|
||||||
if (!mddev->persistent) {
|
if (!mddev->persistent) {
|
||||||
if (info->state & (1<<MD_DISK_SYNC) &&
|
if (info->state & (1<<MD_DISK_SYNC) &&
|
||||||
info->raid_disk < mddev->raid_disks)
|
info->raid_disk < mddev->raid_disks) {
|
||||||
rdev->raid_disk = info->raid_disk;
|
rdev->raid_disk = info->raid_disk;
|
||||||
else
|
set_bit(In_sync, &rdev->flags);
|
||||||
|
} else
|
||||||
rdev->raid_disk = -1;
|
rdev->raid_disk = -1;
|
||||||
} else
|
} else
|
||||||
super_types[mddev->major_version].
|
super_types[mddev->major_version].
|
||||||
|
@ -5230,7 +5261,7 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
|
||||||
printk(KERN_INFO "md: nonpersistent superblock ...\n");
|
printk(KERN_INFO "md: nonpersistent superblock ...\n");
|
||||||
rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512;
|
rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512;
|
||||||
} else
|
} else
|
||||||
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
rdev->sb_start = calc_dev_sboffset(rdev);
|
||||||
rdev->sectors = rdev->sb_start;
|
rdev->sectors = rdev->sb_start;
|
||||||
|
|
||||||
err = bind_rdev_to_array(rdev, mddev);
|
err = bind_rdev_to_array(rdev, mddev);
|
||||||
|
@ -5297,7 +5328,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mddev->persistent)
|
if (mddev->persistent)
|
||||||
rdev->sb_start = calc_dev_sboffset(rdev->bdev);
|
rdev->sb_start = calc_dev_sboffset(rdev);
|
||||||
else
|
else
|
||||||
rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512;
|
rdev->sb_start = i_size_read(rdev->bdev->bd_inode) / 512;
|
||||||
|
|
||||||
|
@ -5510,7 +5541,6 @@ static int update_size(mddev_t *mddev, sector_t num_sectors)
|
||||||
* sb_start or, if that is <data_offset, it must fit before the size
|
* sb_start or, if that is <data_offset, it must fit before the size
|
||||||
* of each device. If num_sectors is zero, we find the largest size
|
* of each device. If num_sectors is zero, we find the largest size
|
||||||
* that fits.
|
* that fits.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
if (mddev->sync_thread)
|
if (mddev->sync_thread)
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
|
@ -6033,7 +6063,8 @@ static int md_thread(void * arg)
|
||||||
|| kthread_should_stop(),
|
|| kthread_should_stop(),
|
||||||
thread->timeout);
|
thread->timeout);
|
||||||
|
|
||||||
if (test_and_clear_bit(THREAD_WAKEUP, &thread->flags))
|
clear_bit(THREAD_WAKEUP, &thread->flags);
|
||||||
|
if (!kthread_should_stop())
|
||||||
thread->run(thread->mddev);
|
thread->run(thread->mddev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6799,7 +6830,7 @@ void md_do_sync(mddev_t *mddev)
|
||||||
desc, mdname(mddev));
|
desc, mdname(mddev));
|
||||||
mddev->curr_resync = j;
|
mddev->curr_resync = j;
|
||||||
}
|
}
|
||||||
mddev->curr_resync_completed = mddev->curr_resync;
|
mddev->curr_resync_completed = j;
|
||||||
|
|
||||||
while (j < max_sectors) {
|
while (j < max_sectors) {
|
||||||
sector_t sectors;
|
sector_t sectors;
|
||||||
|
@ -6817,8 +6848,7 @@ void md_do_sync(mddev_t *mddev)
|
||||||
md_unplug(mddev);
|
md_unplug(mddev);
|
||||||
wait_event(mddev->recovery_wait,
|
wait_event(mddev->recovery_wait,
|
||||||
atomic_read(&mddev->recovery_active) == 0);
|
atomic_read(&mddev->recovery_active) == 0);
|
||||||
mddev->curr_resync_completed =
|
mddev->curr_resync_completed = j;
|
||||||
mddev->curr_resync;
|
|
||||||
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
set_bit(MD_CHANGE_CLEAN, &mddev->flags);
|
||||||
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
|
sysfs_notify(&mddev->kobj, NULL, "sync_completed");
|
||||||
}
|
}
|
||||||
|
@ -7023,6 +7053,45 @@ static int remove_and_add_spares(mddev_t *mddev)
|
||||||
}
|
}
|
||||||
return spares;
|
return spares;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void reap_sync_thread(mddev_t *mddev)
|
||||||
|
{
|
||||||
|
mdk_rdev_t *rdev;
|
||||||
|
|
||||||
|
/* resync has finished, collect result */
|
||||||
|
md_unregister_thread(mddev->sync_thread);
|
||||||
|
mddev->sync_thread = NULL;
|
||||||
|
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
|
||||||
|
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
|
||||||
|
/* success...*/
|
||||||
|
/* activate any spares */
|
||||||
|
if (mddev->pers->spare_active(mddev))
|
||||||
|
sysfs_notify(&mddev->kobj, NULL,
|
||||||
|
"degraded");
|
||||||
|
}
|
||||||
|
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
|
||||||
|
mddev->pers->finish_reshape)
|
||||||
|
mddev->pers->finish_reshape(mddev);
|
||||||
|
md_update_sb(mddev, 1);
|
||||||
|
|
||||||
|
/* if array is no-longer degraded, then any saved_raid_disk
|
||||||
|
* information must be scrapped
|
||||||
|
*/
|
||||||
|
if (!mddev->degraded)
|
||||||
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
||||||
|
rdev->saved_raid_disk = -1;
|
||||||
|
|
||||||
|
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
|
||||||
|
/* flag recovery needed just to double check */
|
||||||
|
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
||||||
|
sysfs_notify_dirent_safe(mddev->sysfs_action);
|
||||||
|
md_new_event(mddev);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This routine is regularly called by all per-raid-array threads to
|
* This routine is regularly called by all per-raid-array threads to
|
||||||
* deal with generic issues like resync and super-block update.
|
* deal with generic issues like resync and super-block update.
|
||||||
|
@ -7047,9 +7116,6 @@ static int remove_and_add_spares(mddev_t *mddev)
|
||||||
*/
|
*/
|
||||||
void md_check_recovery(mddev_t *mddev)
|
void md_check_recovery(mddev_t *mddev)
|
||||||
{
|
{
|
||||||
mdk_rdev_t *rdev;
|
|
||||||
|
|
||||||
|
|
||||||
if (mddev->bitmap)
|
if (mddev->bitmap)
|
||||||
bitmap_daemon_work(mddev);
|
bitmap_daemon_work(mddev);
|
||||||
|
|
||||||
|
@ -7117,34 +7183,7 @@ void md_check_recovery(mddev_t *mddev)
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
if (mddev->sync_thread) {
|
if (mddev->sync_thread) {
|
||||||
/* resync has finished, collect result */
|
reap_sync_thread(mddev);
|
||||||
md_unregister_thread(mddev->sync_thread);
|
|
||||||
mddev->sync_thread = NULL;
|
|
||||||
if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) &&
|
|
||||||
!test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
|
|
||||||
/* success...*/
|
|
||||||
/* activate any spares */
|
|
||||||
if (mddev->pers->spare_active(mddev))
|
|
||||||
sysfs_notify(&mddev->kobj, NULL,
|
|
||||||
"degraded");
|
|
||||||
}
|
|
||||||
if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) &&
|
|
||||||
mddev->pers->finish_reshape)
|
|
||||||
mddev->pers->finish_reshape(mddev);
|
|
||||||
md_update_sb(mddev, 1);
|
|
||||||
|
|
||||||
/* if array is no-longer degraded, then any saved_raid_disk
|
|
||||||
* information must be scrapped
|
|
||||||
*/
|
|
||||||
if (!mddev->degraded)
|
|
||||||
list_for_each_entry(rdev, &mddev->disks, same_set)
|
|
||||||
rdev->saved_raid_disk = -1;
|
|
||||||
|
|
||||||
mddev->recovery = 0;
|
|
||||||
/* flag recovery needed just to double check */
|
|
||||||
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
|
|
||||||
sysfs_notify_dirent_safe(mddev->sysfs_action);
|
|
||||||
md_new_event(mddev);
|
|
||||||
goto unlock;
|
goto unlock;
|
||||||
}
|
}
|
||||||
/* Set RUNNING before clearing NEEDED to avoid
|
/* Set RUNNING before clearing NEEDED to avoid
|
||||||
|
@ -7202,7 +7241,11 @@ void md_check_recovery(mddev_t *mddev)
|
||||||
" thread...\n",
|
" thread...\n",
|
||||||
mdname(mddev));
|
mdname(mddev));
|
||||||
/* leave the spares where they are, it shouldn't hurt */
|
/* leave the spares where they are, it shouldn't hurt */
|
||||||
mddev->recovery = 0;
|
clear_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_RESHAPE, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_REQUESTED, &mddev->recovery);
|
||||||
|
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
|
||||||
} else
|
} else
|
||||||
md_wakeup_thread(mddev->sync_thread);
|
md_wakeup_thread(mddev->sync_thread);
|
||||||
sysfs_notify_dirent_safe(mddev->sysfs_action);
|
sysfs_notify_dirent_safe(mddev->sysfs_action);
|
||||||
|
|
|
@ -60,6 +60,12 @@ struct mdk_rdev_s
|
||||||
mddev_t *mddev; /* RAID array if running */
|
mddev_t *mddev; /* RAID array if running */
|
||||||
int last_events; /* IO event timestamp */
|
int last_events; /* IO event timestamp */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If meta_bdev is non-NULL, it means that a separate device is
|
||||||
|
* being used to store the metadata (superblock/bitmap) which
|
||||||
|
* would otherwise be contained on the same device as the data (bdev).
|
||||||
|
*/
|
||||||
|
struct block_device *meta_bdev;
|
||||||
struct block_device *bdev; /* block device handle */
|
struct block_device *bdev; /* block device handle */
|
||||||
|
|
||||||
struct page *sb_page;
|
struct page *sb_page;
|
||||||
|
@ -148,7 +154,8 @@ struct mddev_s
|
||||||
* are happening, so run/
|
* are happening, so run/
|
||||||
* takeover/stop are not safe
|
* takeover/stop are not safe
|
||||||
*/
|
*/
|
||||||
|
int ready; /* See when safe to pass
|
||||||
|
* IO requests down */
|
||||||
struct gendisk *gendisk;
|
struct gendisk *gendisk;
|
||||||
|
|
||||||
struct kobject kobj;
|
struct kobject kobj;
|
||||||
|
@ -497,8 +504,8 @@ extern void md_flush_request(mddev_t *mddev, struct bio *bio);
|
||||||
extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
|
extern void md_super_write(mddev_t *mddev, mdk_rdev_t *rdev,
|
||||||
sector_t sector, int size, struct page *page);
|
sector_t sector, int size, struct page *page);
|
||||||
extern void md_super_wait(mddev_t *mddev);
|
extern void md_super_wait(mddev_t *mddev);
|
||||||
extern int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
|
extern int sync_page_io(mdk_rdev_t *rdev, sector_t sector, int size,
|
||||||
struct page *page, int rw);
|
struct page *page, int rw, bool metadata_op);
|
||||||
extern void md_do_sync(mddev_t *mddev);
|
extern void md_do_sync(mddev_t *mddev);
|
||||||
extern void md_new_event(mddev_t *mddev);
|
extern void md_new_event(mddev_t *mddev);
|
||||||
extern int md_allow_write(mddev_t *mddev);
|
extern int md_allow_write(mddev_t *mddev);
|
||||||
|
|
|
@ -1027,8 +1027,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
|
||||||
} else
|
} else
|
||||||
set_bit(Faulty, &rdev->flags);
|
set_bit(Faulty, &rdev->flags);
|
||||||
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
||||||
printk(KERN_ALERT "md/raid1:%s: Disk failure on %s, disabling device.\n"
|
printk(KERN_ALERT
|
||||||
KERN_ALERT "md/raid1:%s: Operation continuing on %d devices.\n",
|
"md/raid1:%s: Disk failure on %s, disabling device.\n"
|
||||||
|
"md/raid1:%s: Operation continuing on %d devices.\n",
|
||||||
mdname(mddev), bdevname(rdev->bdev, b),
|
mdname(mddev), bdevname(rdev->bdev, b),
|
||||||
mdname(mddev), conf->raid_disks - mddev->degraded);
|
mdname(mddev), conf->raid_disks - mddev->degraded);
|
||||||
}
|
}
|
||||||
|
@ -1364,10 +1365,10 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
|
||||||
*/
|
*/
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev,
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9,
|
s<<9,
|
||||||
bio->bi_io_vec[idx].bv_page,
|
bio->bi_io_vec[idx].bv_page,
|
||||||
READ)) {
|
READ, false)) {
|
||||||
success = 1;
|
success = 1;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1390,10 +1391,10 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
atomic_add(s, &rdev->corrected_errors);
|
atomic_add(s, &rdev->corrected_errors);
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev,
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9,
|
s<<9,
|
||||||
bio->bi_io_vec[idx].bv_page,
|
bio->bi_io_vec[idx].bv_page,
|
||||||
WRITE) == 0)
|
WRITE, false) == 0)
|
||||||
md_error(mddev, rdev);
|
md_error(mddev, rdev);
|
||||||
}
|
}
|
||||||
d = start;
|
d = start;
|
||||||
|
@ -1405,10 +1406,10 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
|
||||||
continue;
|
continue;
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev,
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9,
|
s<<9,
|
||||||
bio->bi_io_vec[idx].bv_page,
|
bio->bi_io_vec[idx].bv_page,
|
||||||
READ) == 0)
|
READ, false) == 0)
|
||||||
md_error(mddev, rdev);
|
md_error(mddev, rdev);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -1488,10 +1489,8 @@ static void fix_read_error(conf_t *conf, int read_disk,
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
if (rdev &&
|
if (rdev &&
|
||||||
test_bit(In_sync, &rdev->flags) &&
|
test_bit(In_sync, &rdev->flags) &&
|
||||||
sync_page_io(rdev,
|
sync_page_io(rdev, sect, s<<9,
|
||||||
sect + rdev->data_offset,
|
conf->tmppage, READ, false))
|
||||||
s<<9,
|
|
||||||
conf->tmppage, READ))
|
|
||||||
success = 1;
|
success = 1;
|
||||||
else {
|
else {
|
||||||
d++;
|
d++;
|
||||||
|
@ -1514,9 +1513,8 @@ static void fix_read_error(conf_t *conf, int read_disk,
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
if (rdev &&
|
if (rdev &&
|
||||||
test_bit(In_sync, &rdev->flags)) {
|
test_bit(In_sync, &rdev->flags)) {
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev, sect, s<<9,
|
||||||
sect + rdev->data_offset,
|
conf->tmppage, WRITE, false)
|
||||||
s<<9, conf->tmppage, WRITE)
|
|
||||||
== 0)
|
== 0)
|
||||||
/* Well, this device is dead */
|
/* Well, this device is dead */
|
||||||
md_error(mddev, rdev);
|
md_error(mddev, rdev);
|
||||||
|
@ -1531,9 +1529,8 @@ static void fix_read_error(conf_t *conf, int read_disk,
|
||||||
rdev = conf->mirrors[d].rdev;
|
rdev = conf->mirrors[d].rdev;
|
||||||
if (rdev &&
|
if (rdev &&
|
||||||
test_bit(In_sync, &rdev->flags)) {
|
test_bit(In_sync, &rdev->flags)) {
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev, sect, s<<9,
|
||||||
sect + rdev->data_offset,
|
conf->tmppage, READ, false)
|
||||||
s<<9, conf->tmppage, READ)
|
|
||||||
== 0)
|
== 0)
|
||||||
/* Well, this device is dead */
|
/* Well, this device is dead */
|
||||||
md_error(mddev, rdev);
|
md_error(mddev, rdev);
|
||||||
|
|
|
@ -1051,8 +1051,9 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
|
||||||
}
|
}
|
||||||
set_bit(Faulty, &rdev->flags);
|
set_bit(Faulty, &rdev->flags);
|
||||||
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
||||||
printk(KERN_ALERT "md/raid10:%s: Disk failure on %s, disabling device.\n"
|
printk(KERN_ALERT
|
||||||
KERN_ALERT "md/raid10:%s: Operation continuing on %d devices.\n",
|
"md/raid10:%s: Disk failure on %s, disabling device.\n"
|
||||||
|
"md/raid10:%s: Operation continuing on %d devices.\n",
|
||||||
mdname(mddev), bdevname(rdev->bdev, b),
|
mdname(mddev), bdevname(rdev->bdev, b),
|
||||||
mdname(mddev), conf->raid_disks - mddev->degraded);
|
mdname(mddev), conf->raid_disks - mddev->degraded);
|
||||||
}
|
}
|
||||||
|
@ -1559,9 +1560,9 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
success = sync_page_io(rdev,
|
success = sync_page_io(rdev,
|
||||||
r10_bio->devs[sl].addr +
|
r10_bio->devs[sl].addr +
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9,
|
s<<9,
|
||||||
conf->tmppage, READ);
|
conf->tmppage, READ, false);
|
||||||
rdev_dec_pending(rdev, mddev);
|
rdev_dec_pending(rdev, mddev);
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
if (success)
|
if (success)
|
||||||
|
@ -1598,8 +1599,8 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
|
||||||
atomic_add(s, &rdev->corrected_errors);
|
atomic_add(s, &rdev->corrected_errors);
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev,
|
||||||
r10_bio->devs[sl].addr +
|
r10_bio->devs[sl].addr +
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9, conf->tmppage, WRITE)
|
s<<9, conf->tmppage, WRITE, false)
|
||||||
== 0) {
|
== 0) {
|
||||||
/* Well, this device is dead */
|
/* Well, this device is dead */
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
|
@ -1635,9 +1636,9 @@ static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
if (sync_page_io(rdev,
|
if (sync_page_io(rdev,
|
||||||
r10_bio->devs[sl].addr +
|
r10_bio->devs[sl].addr +
|
||||||
sect + rdev->data_offset,
|
sect,
|
||||||
s<<9, conf->tmppage,
|
s<<9, conf->tmppage,
|
||||||
READ) == 0) {
|
READ, false) == 0) {
|
||||||
/* Well, this device is dead */
|
/* Well, this device is dead */
|
||||||
printk(KERN_NOTICE
|
printk(KERN_NOTICE
|
||||||
"md/raid10:%s: unable to read back "
|
"md/raid10:%s: unable to read back "
|
||||||
|
|
|
@ -1721,7 +1721,6 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
|
||||||
set_bit(Faulty, &rdev->flags);
|
set_bit(Faulty, &rdev->flags);
|
||||||
printk(KERN_ALERT
|
printk(KERN_ALERT
|
||||||
"md/raid:%s: Disk failure on %s, disabling device.\n"
|
"md/raid:%s: Disk failure on %s, disabling device.\n"
|
||||||
KERN_ALERT
|
|
||||||
"md/raid:%s: Operation continuing on %d devices.\n",
|
"md/raid:%s: Operation continuing on %d devices.\n",
|
||||||
mdname(mddev),
|
mdname(mddev),
|
||||||
bdevname(rdev->bdev, b),
|
bdevname(rdev->bdev, b),
|
||||||
|
@ -4237,7 +4236,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
|
||||||
wait_event(conf->wait_for_overlap,
|
wait_event(conf->wait_for_overlap,
|
||||||
atomic_read(&conf->reshape_stripes)==0);
|
atomic_read(&conf->reshape_stripes)==0);
|
||||||
mddev->reshape_position = conf->reshape_progress;
|
mddev->reshape_position = conf->reshape_progress;
|
||||||
mddev->curr_resync_completed = mddev->curr_resync;
|
mddev->curr_resync_completed = sector_nr;
|
||||||
conf->reshape_checkpoint = jiffies;
|
conf->reshape_checkpoint = jiffies;
|
||||||
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
||||||
md_wakeup_thread(mddev->thread);
|
md_wakeup_thread(mddev->thread);
|
||||||
|
@ -4338,7 +4337,7 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
|
||||||
wait_event(conf->wait_for_overlap,
|
wait_event(conf->wait_for_overlap,
|
||||||
atomic_read(&conf->reshape_stripes) == 0);
|
atomic_read(&conf->reshape_stripes) == 0);
|
||||||
mddev->reshape_position = conf->reshape_progress;
|
mddev->reshape_position = conf->reshape_progress;
|
||||||
mddev->curr_resync_completed = mddev->curr_resync + reshape_sectors;
|
mddev->curr_resync_completed = sector_nr;
|
||||||
conf->reshape_checkpoint = jiffies;
|
conf->reshape_checkpoint = jiffies;
|
||||||
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
set_bit(MD_CHANGE_DEVS, &mddev->flags);
|
||||||
md_wakeup_thread(mddev->thread);
|
md_wakeup_thread(mddev->thread);
|
||||||
|
@ -5339,7 +5338,7 @@ static int raid5_spare_active(mddev_t *mddev)
|
||||||
&& !test_bit(Faulty, &tmp->rdev->flags)
|
&& !test_bit(Faulty, &tmp->rdev->flags)
|
||||||
&& !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
|
&& !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
|
||||||
count++;
|
count++;
|
||||||
sysfs_notify_dirent(tmp->rdev->sysfs_state);
|
sysfs_notify_dirent_safe(tmp->rdev->sysfs_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
spin_lock_irqsave(&conf->device_lock, flags);
|
spin_lock_irqsave(&conf->device_lock, flags);
|
||||||
|
@ -5528,8 +5527,8 @@ static int raid5_start_reshape(mddev_t *mddev)
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
|
|
||||||
list_for_each_entry(rdev, &mddev->disks, same_set)
|
list_for_each_entry(rdev, &mddev->disks, same_set)
|
||||||
if (rdev->raid_disk < 0 &&
|
if ((rdev->raid_disk < 0 || rdev->raid_disk >= conf->raid_disks)
|
||||||
!test_bit(Faulty, &rdev->flags))
|
&& !test_bit(Faulty, &rdev->flags))
|
||||||
spares++;
|
spares++;
|
||||||
|
|
||||||
if (spares - mddev->degraded < mddev->delta_disks - conf->max_degraded)
|
if (spares - mddev->degraded < mddev->delta_disks - conf->max_degraded)
|
||||||
|
@ -5589,6 +5588,11 @@ static int raid5_start_reshape(mddev_t *mddev)
|
||||||
/* Failure here is OK */;
|
/* Failure here is OK */;
|
||||||
} else
|
} else
|
||||||
break;
|
break;
|
||||||
|
} else if (rdev->raid_disk >= conf->previous_raid_disks
|
||||||
|
&& !test_bit(Faulty, &rdev->flags)) {
|
||||||
|
/* This is a spare that was manually added */
|
||||||
|
set_bit(In_sync, &rdev->flags);
|
||||||
|
added_devices++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* When a reshape changes the number of devices, ->degraded
|
/* When a reshape changes the number of devices, ->degraded
|
||||||
|
|
|
@ -1732,6 +1732,11 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
|
||||||
device_init_wakeup(&pdev->dev, 1);
|
device_init_wakeup(&pdev->dev, 1);
|
||||||
platform_set_drvdata(pdev, port);
|
platform_set_drvdata(pdev, port);
|
||||||
|
|
||||||
|
if (port->rs485.flags & SER_RS485_ENABLED) {
|
||||||
|
UART_PUT_MR(&port->uart, ATMEL_US_USMODE_NORMAL);
|
||||||
|
UART_PUT_CR(&port->uart, ATMEL_US_RTSEN);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_add_port:
|
err_add_port:
|
||||||
|
|
|
@ -71,11 +71,18 @@ config XEN_SYS_HYPERVISOR
|
||||||
but will have no xen contents.
|
but will have no xen contents.
|
||||||
|
|
||||||
config XEN_XENBUS_FRONTEND
|
config XEN_XENBUS_FRONTEND
|
||||||
tristate
|
tristate
|
||||||
|
|
||||||
|
config XEN_GNTDEV
|
||||||
|
tristate "userspace grant access device driver"
|
||||||
|
depends on XEN
|
||||||
|
select MMU_NOTIFIER
|
||||||
|
help
|
||||||
|
Allows userspace processes to use grants.
|
||||||
|
|
||||||
config XEN_PLATFORM_PCI
|
config XEN_PLATFORM_PCI
|
||||||
tristate "xen platform pci device driver"
|
tristate "xen platform pci device driver"
|
||||||
depends on XEN_PVHVM
|
depends on XEN_PVHVM && PCI
|
||||||
default m
|
default m
|
||||||
help
|
help
|
||||||
Driver for the Xen PCI Platform device: it is responsible for
|
Driver for the Xen PCI Platform device: it is responsible for
|
||||||
|
|
|
@ -9,11 +9,14 @@ obj-$(CONFIG_HOTPLUG_CPU) += cpu_hotplug.o
|
||||||
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
|
obj-$(CONFIG_XEN_XENCOMM) += xencomm.o
|
||||||
obj-$(CONFIG_XEN_BALLOON) += balloon.o
|
obj-$(CONFIG_XEN_BALLOON) += balloon.o
|
||||||
obj-$(CONFIG_XEN_DEV_EVTCHN) += xen-evtchn.o
|
obj-$(CONFIG_XEN_DEV_EVTCHN) += xen-evtchn.o
|
||||||
|
obj-$(CONFIG_XEN_GNTDEV) += xen-gntdev.o
|
||||||
obj-$(CONFIG_XENFS) += xenfs/
|
obj-$(CONFIG_XENFS) += xenfs/
|
||||||
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
|
obj-$(CONFIG_XEN_SYS_HYPERVISOR) += sys-hypervisor.o
|
||||||
obj-$(CONFIG_XEN_PLATFORM_PCI) += platform-pci.o
|
obj-$(CONFIG_XEN_PLATFORM_PCI) += xen-platform-pci.o
|
||||||
obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
|
obj-$(CONFIG_SWIOTLB_XEN) += swiotlb-xen.o
|
||||||
obj-$(CONFIG_XEN_DOM0) += pci.o
|
obj-$(CONFIG_XEN_DOM0) += pci.o
|
||||||
|
|
||||||
xen-evtchn-y := evtchn.o
|
xen-evtchn-y := evtchn.o
|
||||||
|
xen-gntdev-y := gntdev.o
|
||||||
|
|
||||||
|
xen-platform-pci-y := platform-pci.o
|
||||||
|
|
665
drivers/xen/gntdev.c
Normal file
665
drivers/xen/gntdev.c
Normal file
|
@ -0,0 +1,665 @@
|
||||||
|
/******************************************************************************
|
||||||
|
* gntdev.c
|
||||||
|
*
|
||||||
|
* Device for accessing (in user-space) pages that have been granted by other
|
||||||
|
* domains.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2006-2007, D G Murray.
|
||||||
|
* (c) 2009 Gerd Hoffmann <kraxel@redhat.com>
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program; if not, write to the Free Software
|
||||||
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||||
|
*/
|
||||||
|
|
||||||
|
#undef DEBUG
|
||||||
|
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/miscdevice.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mman.h>
|
||||||
|
#include <linux/mmu_notifier.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
|
||||||
|
#include <xen/xen.h>
|
||||||
|
#include <xen/grant_table.h>
|
||||||
|
#include <xen/gntdev.h>
|
||||||
|
#include <asm/xen/hypervisor.h>
|
||||||
|
#include <asm/xen/hypercall.h>
|
||||||
|
#include <asm/xen/page.h>
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_AUTHOR("Derek G. Murray <Derek.Murray@cl.cam.ac.uk>, "
|
||||||
|
"Gerd Hoffmann <kraxel@redhat.com>");
|
||||||
|
MODULE_DESCRIPTION("User-space granted page access driver");
|
||||||
|
|
||||||
|
static int limit = 1024;
|
||||||
|
module_param(limit, int, 0644);
|
||||||
|
MODULE_PARM_DESC(limit, "Maximum number of grants that may be mapped at "
|
||||||
|
"once by a gntdev instance");
|
||||||
|
|
||||||
|
struct gntdev_priv {
|
||||||
|
struct list_head maps;
|
||||||
|
uint32_t used;
|
||||||
|
uint32_t limit;
|
||||||
|
/* lock protects maps from concurrent changes */
|
||||||
|
spinlock_t lock;
|
||||||
|
struct mm_struct *mm;
|
||||||
|
struct mmu_notifier mn;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct grant_map {
|
||||||
|
struct list_head next;
|
||||||
|
struct gntdev_priv *priv;
|
||||||
|
struct vm_area_struct *vma;
|
||||||
|
int index;
|
||||||
|
int count;
|
||||||
|
int flags;
|
||||||
|
int is_mapped;
|
||||||
|
struct ioctl_gntdev_grant_ref *grants;
|
||||||
|
struct gnttab_map_grant_ref *map_ops;
|
||||||
|
struct gnttab_unmap_grant_ref *unmap_ops;
|
||||||
|
struct page **pages;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void gntdev_print_maps(struct gntdev_priv *priv,
|
||||||
|
char *text, int text_index)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
struct grant_map *map;
|
||||||
|
|
||||||
|
pr_debug("maps list (priv %p, usage %d/%d)\n",
|
||||||
|
priv, priv->used, priv->limit);
|
||||||
|
|
||||||
|
list_for_each_entry(map, &priv->maps, next)
|
||||||
|
pr_debug(" index %2d, count %2d %s\n",
|
||||||
|
map->index, map->count,
|
||||||
|
map->index == text_index && text ? text : "");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct grant_map *gntdev_alloc_map(struct gntdev_priv *priv, int count)
|
||||||
|
{
|
||||||
|
struct grant_map *add;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
add = kzalloc(sizeof(struct grant_map), GFP_KERNEL);
|
||||||
|
if (NULL == add)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
add->grants = kzalloc(sizeof(add->grants[0]) * count, GFP_KERNEL);
|
||||||
|
add->map_ops = kzalloc(sizeof(add->map_ops[0]) * count, GFP_KERNEL);
|
||||||
|
add->unmap_ops = kzalloc(sizeof(add->unmap_ops[0]) * count, GFP_KERNEL);
|
||||||
|
add->pages = kzalloc(sizeof(add->pages[0]) * count, GFP_KERNEL);
|
||||||
|
if (NULL == add->grants ||
|
||||||
|
NULL == add->map_ops ||
|
||||||
|
NULL == add->unmap_ops ||
|
||||||
|
NULL == add->pages)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
add->pages[i] = alloc_page(GFP_KERNEL | __GFP_HIGHMEM);
|
||||||
|
if (add->pages[i] == NULL)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
add->index = 0;
|
||||||
|
add->count = count;
|
||||||
|
add->priv = priv;
|
||||||
|
|
||||||
|
if (add->count + priv->used > priv->limit)
|
||||||
|
goto err;
|
||||||
|
|
||||||
|
return add;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (add->pages)
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
if (add->pages[i])
|
||||||
|
__free_page(add->pages[i]);
|
||||||
|
}
|
||||||
|
kfree(add->pages);
|
||||||
|
kfree(add->grants);
|
||||||
|
kfree(add->map_ops);
|
||||||
|
kfree(add->unmap_ops);
|
||||||
|
kfree(add);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gntdev_add_map(struct gntdev_priv *priv, struct grant_map *add)
|
||||||
|
{
|
||||||
|
struct grant_map *map;
|
||||||
|
|
||||||
|
list_for_each_entry(map, &priv->maps, next) {
|
||||||
|
if (add->index + add->count < map->index) {
|
||||||
|
list_add_tail(&add->next, &map->next);
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
add->index = map->index + map->count;
|
||||||
|
}
|
||||||
|
list_add_tail(&add->next, &priv->maps);
|
||||||
|
|
||||||
|
done:
|
||||||
|
priv->used += add->count;
|
||||||
|
gntdev_print_maps(priv, "[new]", add->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct grant_map *gntdev_find_map_index(struct gntdev_priv *priv,
|
||||||
|
int index, int count)
|
||||||
|
{
|
||||||
|
struct grant_map *map;
|
||||||
|
|
||||||
|
list_for_each_entry(map, &priv->maps, next) {
|
||||||
|
if (map->index != index)
|
||||||
|
continue;
|
||||||
|
if (map->count != count)
|
||||||
|
continue;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct grant_map *gntdev_find_map_vaddr(struct gntdev_priv *priv,
|
||||||
|
unsigned long vaddr)
|
||||||
|
{
|
||||||
|
struct grant_map *map;
|
||||||
|
|
||||||
|
list_for_each_entry(map, &priv->maps, next) {
|
||||||
|
if (!map->vma)
|
||||||
|
continue;
|
||||||
|
if (vaddr < map->vma->vm_start)
|
||||||
|
continue;
|
||||||
|
if (vaddr >= map->vma->vm_end)
|
||||||
|
continue;
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gntdev_del_map(struct grant_map *map)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (map->vma)
|
||||||
|
return -EBUSY;
|
||||||
|
for (i = 0; i < map->count; i++)
|
||||||
|
if (map->unmap_ops[i].handle)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
|
map->priv->used -= map->count;
|
||||||
|
list_del(&map->next);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void gntdev_free_map(struct grant_map *map)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!map)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (map->pages)
|
||||||
|
for (i = 0; i < map->count; i++) {
|
||||||
|
if (map->pages[i])
|
||||||
|
__free_page(map->pages[i]);
|
||||||
|
}
|
||||||
|
kfree(map->pages);
|
||||||
|
kfree(map->grants);
|
||||||
|
kfree(map->map_ops);
|
||||||
|
kfree(map->unmap_ops);
|
||||||
|
kfree(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static int find_grant_ptes(pte_t *pte, pgtable_t token,
|
||||||
|
unsigned long addr, void *data)
|
||||||
|
{
|
||||||
|
struct grant_map *map = data;
|
||||||
|
unsigned int pgnr = (addr - map->vma->vm_start) >> PAGE_SHIFT;
|
||||||
|
u64 pte_maddr;
|
||||||
|
|
||||||
|
BUG_ON(pgnr >= map->count);
|
||||||
|
pte_maddr = arbitrary_virt_to_machine(pte).maddr;
|
||||||
|
|
||||||
|
gnttab_set_map_op(&map->map_ops[pgnr], pte_maddr,
|
||||||
|
GNTMAP_contains_pte | map->flags,
|
||||||
|
map->grants[pgnr].ref,
|
||||||
|
map->grants[pgnr].domid);
|
||||||
|
gnttab_set_unmap_op(&map->unmap_ops[pgnr], pte_maddr,
|
||||||
|
GNTMAP_contains_pte | map->flags,
|
||||||
|
0 /* handle */);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int map_grant_pages(struct grant_map *map)
|
||||||
|
{
|
||||||
|
int i, err = 0;
|
||||||
|
|
||||||
|
pr_debug("map %d+%d\n", map->index, map->count);
|
||||||
|
err = gnttab_map_refs(map->map_ops, map->pages, map->count);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < map->count; i++) {
|
||||||
|
if (map->map_ops[i].status)
|
||||||
|
err = -EINVAL;
|
||||||
|
map->unmap_ops[i].handle = map->map_ops[i].handle;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int unmap_grant_pages(struct grant_map *map, int offset, int pages)
|
||||||
|
{
|
||||||
|
int i, err = 0;
|
||||||
|
|
||||||
|
pr_debug("map %d+%d [%d+%d]\n", map->index, map->count, offset, pages);
|
||||||
|
err = gnttab_unmap_refs(map->unmap_ops + offset, map->pages, pages);
|
||||||
|
if (err)
|
||||||
|
return err;
|
||||||
|
|
||||||
|
for (i = 0; i < pages; i++) {
|
||||||
|
if (map->unmap_ops[offset+i].status)
|
||||||
|
err = -EINVAL;
|
||||||
|
map->unmap_ops[offset+i].handle = 0;
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void gntdev_vma_close(struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct grant_map *map = vma->vm_private_data;
|
||||||
|
|
||||||
|
pr_debug("close %p\n", vma);
|
||||||
|
map->is_mapped = 0;
|
||||||
|
map->vma = NULL;
|
||||||
|
vma->vm_private_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gntdev_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
|
||||||
|
{
|
||||||
|
pr_debug("vaddr %p, pgoff %ld (shouldn't happen)\n",
|
||||||
|
vmf->virtual_address, vmf->pgoff);
|
||||||
|
vmf->flags = VM_FAULT_ERROR;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct vm_operations_struct gntdev_vmops = {
|
||||||
|
.close = gntdev_vma_close,
|
||||||
|
.fault = gntdev_vma_fault,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void mn_invl_range_start(struct mmu_notifier *mn,
|
||||||
|
struct mm_struct *mm,
|
||||||
|
unsigned long start, unsigned long end)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
|
||||||
|
struct grant_map *map;
|
||||||
|
unsigned long mstart, mend;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
list_for_each_entry(map, &priv->maps, next) {
|
||||||
|
if (!map->vma)
|
||||||
|
continue;
|
||||||
|
if (!map->is_mapped)
|
||||||
|
continue;
|
||||||
|
if (map->vma->vm_start >= end)
|
||||||
|
continue;
|
||||||
|
if (map->vma->vm_end <= start)
|
||||||
|
continue;
|
||||||
|
mstart = max(start, map->vma->vm_start);
|
||||||
|
mend = min(end, map->vma->vm_end);
|
||||||
|
pr_debug("map %d+%d (%lx %lx), range %lx %lx, mrange %lx %lx\n",
|
||||||
|
map->index, map->count,
|
||||||
|
map->vma->vm_start, map->vma->vm_end,
|
||||||
|
start, end, mstart, mend);
|
||||||
|
err = unmap_grant_pages(map,
|
||||||
|
(mstart - map->vma->vm_start) >> PAGE_SHIFT,
|
||||||
|
(mend - mstart) >> PAGE_SHIFT);
|
||||||
|
WARN_ON(err);
|
||||||
|
}
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mn_invl_page(struct mmu_notifier *mn,
|
||||||
|
struct mm_struct *mm,
|
||||||
|
unsigned long address)
|
||||||
|
{
|
||||||
|
mn_invl_range_start(mn, mm, address, address + PAGE_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mn_release(struct mmu_notifier *mn,
|
||||||
|
struct mm_struct *mm)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv = container_of(mn, struct gntdev_priv, mn);
|
||||||
|
struct grant_map *map;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
list_for_each_entry(map, &priv->maps, next) {
|
||||||
|
if (!map->vma)
|
||||||
|
continue;
|
||||||
|
pr_debug("map %d+%d (%lx %lx)\n",
|
||||||
|
map->index, map->count,
|
||||||
|
map->vma->vm_start, map->vma->vm_end);
|
||||||
|
err = unmap_grant_pages(map, /* offset */ 0, map->count);
|
||||||
|
WARN_ON(err);
|
||||||
|
}
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct mmu_notifier_ops gntdev_mmu_ops = {
|
||||||
|
.release = mn_release,
|
||||||
|
.invalidate_page = mn_invl_page,
|
||||||
|
.invalidate_range_start = mn_invl_range_start,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static int gntdev_open(struct inode *inode, struct file *flip)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||||
|
if (!priv)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
|
INIT_LIST_HEAD(&priv->maps);
|
||||||
|
spin_lock_init(&priv->lock);
|
||||||
|
priv->limit = limit;
|
||||||
|
|
||||||
|
priv->mm = get_task_mm(current);
|
||||||
|
if (!priv->mm) {
|
||||||
|
kfree(priv);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
priv->mn.ops = &gntdev_mmu_ops;
|
||||||
|
ret = mmu_notifier_register(&priv->mn, priv->mm);
|
||||||
|
mmput(priv->mm);
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
kfree(priv);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
flip->private_data = priv;
|
||||||
|
pr_debug("priv %p\n", priv);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gntdev_release(struct inode *inode, struct file *flip)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv = flip->private_data;
|
||||||
|
struct grant_map *map;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
pr_debug("priv %p\n", priv);
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
while (!list_empty(&priv->maps)) {
|
||||||
|
map = list_entry(priv->maps.next, struct grant_map, next);
|
||||||
|
err = gntdev_del_map(map);
|
||||||
|
if (WARN_ON(err))
|
||||||
|
gntdev_free_map(map);
|
||||||
|
|
||||||
|
}
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
|
mmu_notifier_unregister(&priv->mn, priv->mm);
|
||||||
|
kfree(priv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gntdev_ioctl_map_grant_ref(struct gntdev_priv *priv,
|
||||||
|
struct ioctl_gntdev_map_grant_ref __user *u)
|
||||||
|
{
|
||||||
|
struct ioctl_gntdev_map_grant_ref op;
|
||||||
|
struct grant_map *map;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (copy_from_user(&op, u, sizeof(op)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
pr_debug("priv %p, add %d\n", priv, op.count);
|
||||||
|
if (unlikely(op.count <= 0))
|
||||||
|
return -EINVAL;
|
||||||
|
if (unlikely(op.count > priv->limit))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
err = -ENOMEM;
|
||||||
|
map = gntdev_alloc_map(priv, op.count);
|
||||||
|
if (!map)
|
||||||
|
return err;
|
||||||
|
if (copy_from_user(map->grants, &u->refs,
|
||||||
|
sizeof(map->grants[0]) * op.count) != 0) {
|
||||||
|
gntdev_free_map(map);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
gntdev_add_map(priv, map);
|
||||||
|
op.index = map->index << PAGE_SHIFT;
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
|
if (copy_to_user(u, &op, sizeof(op)) != 0) {
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
gntdev_del_map(map);
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
gntdev_free_map(map);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gntdev_ioctl_unmap_grant_ref(struct gntdev_priv *priv,
|
||||||
|
struct ioctl_gntdev_unmap_grant_ref __user *u)
|
||||||
|
{
|
||||||
|
struct ioctl_gntdev_unmap_grant_ref op;
|
||||||
|
struct grant_map *map;
|
||||||
|
int err = -ENOENT;
|
||||||
|
|
||||||
|
if (copy_from_user(&op, u, sizeof(op)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
pr_debug("priv %p, del %d+%d\n", priv, (int)op.index, (int)op.count);
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
map = gntdev_find_map_index(priv, op.index >> PAGE_SHIFT, op.count);
|
||||||
|
if (map)
|
||||||
|
err = gntdev_del_map(map);
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
if (!err)
|
||||||
|
gntdev_free_map(map);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gntdev_ioctl_get_offset_for_vaddr(struct gntdev_priv *priv,
|
||||||
|
struct ioctl_gntdev_get_offset_for_vaddr __user *u)
|
||||||
|
{
|
||||||
|
struct ioctl_gntdev_get_offset_for_vaddr op;
|
||||||
|
struct grant_map *map;
|
||||||
|
|
||||||
|
if (copy_from_user(&op, u, sizeof(op)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
pr_debug("priv %p, offset for vaddr %lx\n", priv, (unsigned long)op.vaddr);
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
map = gntdev_find_map_vaddr(priv, op.vaddr);
|
||||||
|
if (map == NULL ||
|
||||||
|
map->vma->vm_start != op.vaddr) {
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
op.offset = map->index << PAGE_SHIFT;
|
||||||
|
op.count = map->count;
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
|
if (copy_to_user(u, &op, sizeof(op)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gntdev_ioctl_set_max_grants(struct gntdev_priv *priv,
|
||||||
|
struct ioctl_gntdev_set_max_grants __user *u)
|
||||||
|
{
|
||||||
|
struct ioctl_gntdev_set_max_grants op;
|
||||||
|
|
||||||
|
if (copy_from_user(&op, u, sizeof(op)) != 0)
|
||||||
|
return -EFAULT;
|
||||||
|
pr_debug("priv %p, limit %d\n", priv, op.count);
|
||||||
|
if (op.count > limit)
|
||||||
|
return -E2BIG;
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
priv->limit = op.count;
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static long gntdev_ioctl(struct file *flip,
|
||||||
|
unsigned int cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv = flip->private_data;
|
||||||
|
void __user *ptr = (void __user *)arg;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
case IOCTL_GNTDEV_MAP_GRANT_REF:
|
||||||
|
return gntdev_ioctl_map_grant_ref(priv, ptr);
|
||||||
|
|
||||||
|
case IOCTL_GNTDEV_UNMAP_GRANT_REF:
|
||||||
|
return gntdev_ioctl_unmap_grant_ref(priv, ptr);
|
||||||
|
|
||||||
|
case IOCTL_GNTDEV_GET_OFFSET_FOR_VADDR:
|
||||||
|
return gntdev_ioctl_get_offset_for_vaddr(priv, ptr);
|
||||||
|
|
||||||
|
case IOCTL_GNTDEV_SET_MAX_GRANTS:
|
||||||
|
return gntdev_ioctl_set_max_grants(priv, ptr);
|
||||||
|
|
||||||
|
default:
|
||||||
|
pr_debug("priv %p, unknown cmd %x\n", priv, cmd);
|
||||||
|
return -ENOIOCTLCMD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gntdev_mmap(struct file *flip, struct vm_area_struct *vma)
|
||||||
|
{
|
||||||
|
struct gntdev_priv *priv = flip->private_data;
|
||||||
|
int index = vma->vm_pgoff;
|
||||||
|
int count = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
|
||||||
|
struct grant_map *map;
|
||||||
|
int err = -EINVAL;
|
||||||
|
|
||||||
|
if ((vma->vm_flags & VM_WRITE) && !(vma->vm_flags & VM_SHARED))
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pr_debug("map %d+%d at %lx (pgoff %lx)\n",
|
||||||
|
index, count, vma->vm_start, vma->vm_pgoff);
|
||||||
|
|
||||||
|
spin_lock(&priv->lock);
|
||||||
|
map = gntdev_find_map_index(priv, index, count);
|
||||||
|
if (!map)
|
||||||
|
goto unlock_out;
|
||||||
|
if (map->vma)
|
||||||
|
goto unlock_out;
|
||||||
|
if (priv->mm != vma->vm_mm) {
|
||||||
|
printk(KERN_WARNING "Huh? Other mm?\n");
|
||||||
|
goto unlock_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
vma->vm_ops = &gntdev_vmops;
|
||||||
|
|
||||||
|
vma->vm_flags |= VM_RESERVED|VM_DONTCOPY|VM_DONTEXPAND|VM_PFNMAP;
|
||||||
|
|
||||||
|
vma->vm_private_data = map;
|
||||||
|
map->vma = vma;
|
||||||
|
|
||||||
|
map->flags = GNTMAP_host_map | GNTMAP_application_map;
|
||||||
|
if (!(vma->vm_flags & VM_WRITE))
|
||||||
|
map->flags |= GNTMAP_readonly;
|
||||||
|
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
|
||||||
|
err = apply_to_page_range(vma->vm_mm, vma->vm_start,
|
||||||
|
vma->vm_end - vma->vm_start,
|
||||||
|
find_grant_ptes, map);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_WARNING "find_grant_ptes() failure.\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = map_grant_pages(map);
|
||||||
|
if (err) {
|
||||||
|
printk(KERN_WARNING "map_grant_pages() failure.\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
map->is_mapped = 1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
unlock_out:
|
||||||
|
spin_unlock(&priv->lock);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct file_operations gntdev_fops = {
|
||||||
|
.owner = THIS_MODULE,
|
||||||
|
.open = gntdev_open,
|
||||||
|
.release = gntdev_release,
|
||||||
|
.mmap = gntdev_mmap,
|
||||||
|
.unlocked_ioctl = gntdev_ioctl
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct miscdevice gntdev_miscdev = {
|
||||||
|
.minor = MISC_DYNAMIC_MINOR,
|
||||||
|
.name = "xen/gntdev",
|
||||||
|
.fops = &gntdev_fops,
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static int __init gntdev_init(void)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
if (!xen_domain())
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
err = misc_register(&gntdev_miscdev);
|
||||||
|
if (err != 0) {
|
||||||
|
printk(KERN_ERR "Could not register gntdev device\n");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit gntdev_exit(void)
|
||||||
|
{
|
||||||
|
misc_deregister(&gntdev_miscdev);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(gntdev_init);
|
||||||
|
module_exit(gntdev_exit);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
|
@ -447,6 +447,52 @@ unsigned int gnttab_max_grant_frames(void)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
|
EXPORT_SYMBOL_GPL(gnttab_max_grant_frames);
|
||||||
|
|
||||||
|
int gnttab_map_refs(struct gnttab_map_grant_ref *map_ops,
|
||||||
|
struct page **pages, unsigned int count)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
pte_t *pte;
|
||||||
|
unsigned long mfn;
|
||||||
|
|
||||||
|
ret = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, map_ops, count);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
/* m2p override only supported for GNTMAP_contains_pte mappings */
|
||||||
|
if (!(map_ops[i].flags & GNTMAP_contains_pte))
|
||||||
|
continue;
|
||||||
|
pte = (pte_t *) (mfn_to_virt(PFN_DOWN(map_ops[i].host_addr)) +
|
||||||
|
(map_ops[i].host_addr & ~PAGE_MASK));
|
||||||
|
mfn = pte_mfn(*pte);
|
||||||
|
ret = m2p_add_override(mfn, pages[i]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gnttab_map_refs);
|
||||||
|
|
||||||
|
int gnttab_unmap_refs(struct gnttab_unmap_grant_ref *unmap_ops,
|
||||||
|
struct page **pages, unsigned int count)
|
||||||
|
{
|
||||||
|
int i, ret;
|
||||||
|
|
||||||
|
ret = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, unmap_ops, count);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
ret = m2p_remove_override(pages[i]);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(gnttab_unmap_refs);
|
||||||
|
|
||||||
static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
|
static int gnttab_map(unsigned int start_idx, unsigned int end_idx)
|
||||||
{
|
{
|
||||||
struct gnttab_setup_table setup;
|
struct gnttab_setup_table setup;
|
||||||
|
|
|
@ -105,7 +105,7 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
|
||||||
const struct pci_device_id *ent)
|
const struct pci_device_id *ent)
|
||||||
{
|
{
|
||||||
int i, ret;
|
int i, ret;
|
||||||
long ioaddr, iolen;
|
long ioaddr;
|
||||||
long mmio_addr, mmio_len;
|
long mmio_addr, mmio_len;
|
||||||
unsigned int max_nr_gframes;
|
unsigned int max_nr_gframes;
|
||||||
|
|
||||||
|
@ -114,7 +114,6 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
|
||||||
return i;
|
return i;
|
||||||
|
|
||||||
ioaddr = pci_resource_start(pdev, 0);
|
ioaddr = pci_resource_start(pdev, 0);
|
||||||
iolen = pci_resource_len(pdev, 0);
|
|
||||||
|
|
||||||
mmio_addr = pci_resource_start(pdev, 1);
|
mmio_addr = pci_resource_start(pdev, 1);
|
||||||
mmio_len = pci_resource_len(pdev, 1);
|
mmio_len = pci_resource_len(pdev, 1);
|
||||||
|
@ -125,19 +124,13 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
|
||||||
goto pci_out;
|
goto pci_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request_mem_region(mmio_addr, mmio_len, DRV_NAME) == NULL) {
|
ret = pci_request_region(pdev, 1, DRV_NAME);
|
||||||
dev_err(&pdev->dev, "MEM I/O resource 0x%lx @ 0x%lx busy\n",
|
if (ret < 0)
|
||||||
mmio_addr, mmio_len);
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto pci_out;
|
goto pci_out;
|
||||||
}
|
|
||||||
|
|
||||||
if (request_region(ioaddr, iolen, DRV_NAME) == NULL) {
|
ret = pci_request_region(pdev, 0, DRV_NAME);
|
||||||
dev_err(&pdev->dev, "I/O resource 0x%lx @ 0x%lx busy\n",
|
if (ret < 0)
|
||||||
iolen, ioaddr);
|
|
||||||
ret = -EBUSY;
|
|
||||||
goto mem_out;
|
goto mem_out;
|
||||||
}
|
|
||||||
|
|
||||||
platform_mmio = mmio_addr;
|
platform_mmio = mmio_addr;
|
||||||
platform_mmiolen = mmio_len;
|
platform_mmiolen = mmio_len;
|
||||||
|
@ -169,9 +162,9 @@ static int __devinit platform_pci_init(struct pci_dev *pdev,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
release_region(ioaddr, iolen);
|
pci_release_region(pdev, 0);
|
||||||
mem_out:
|
mem_out:
|
||||||
release_mem_region(mmio_addr, mmio_len);
|
pci_release_region(pdev, 1);
|
||||||
pci_out:
|
pci_out:
|
||||||
pci_disable_device(pdev);
|
pci_disable_device(pdev);
|
||||||
return ret;
|
return ret;
|
||||||
|
|
|
@ -141,13 +141,12 @@ int ecryptfs_init_persistent_file(struct dentry *ecryptfs_dentry)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inode *ecryptfs_get_inode(struct inode *lower_inode,
|
static struct inode *ecryptfs_get_inode(struct inode *lower_inode,
|
||||||
struct super_block *sb)
|
struct super_block *sb)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
lower_inode = lower_dentry->d_inode;
|
|
||||||
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) {
|
if (lower_inode->i_sb != ecryptfs_superblock_to_lower(sb)) {
|
||||||
rc = -EXDEV;
|
rc = -EXDEV;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -202,7 +201,7 @@ int ecryptfs_interpose(struct dentry *lower_dentry, struct dentry *dentry,
|
||||||
{
|
{
|
||||||
struct inode *lower_inode = lower_dentry->d_inode;
|
struct inode *lower_inode = lower_dentry->d_inode;
|
||||||
struct inode *inode = ecryptfs_get_inode(lower_inode, sb);
|
struct inode *inode = ecryptfs_get_inode(lower_inode, sb);
|
||||||
if (IS_ERR(inode)
|
if (IS_ERR(inode))
|
||||||
return PTR_ERR(inode);
|
return PTR_ERR(inode);
|
||||||
if (flags & ECRYPTFS_INTERPOSE_FLAG_D_ADD)
|
if (flags & ECRYPTFS_INTERPOSE_FLAG_D_ADD)
|
||||||
d_add(dentry, inode);
|
d_add(dentry, inode);
|
||||||
|
|
|
@ -84,13 +84,9 @@ static inline struct inode *wb_inode(struct list_head *head)
|
||||||
return list_entry(head, struct inode, i_wb_list);
|
return list_entry(head, struct inode, i_wb_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bdi_queue_work(struct backing_dev_info *bdi,
|
/* Wakeup flusher thread or forker thread to fork it. Requires bdi->wb_lock. */
|
||||||
struct wb_writeback_work *work)
|
static void bdi_wakeup_flusher(struct backing_dev_info *bdi)
|
||||||
{
|
{
|
||||||
trace_writeback_queue(bdi, work);
|
|
||||||
|
|
||||||
spin_lock_bh(&bdi->wb_lock);
|
|
||||||
list_add_tail(&work->list, &bdi->work_list);
|
|
||||||
if (bdi->wb.task) {
|
if (bdi->wb.task) {
|
||||||
wake_up_process(bdi->wb.task);
|
wake_up_process(bdi->wb.task);
|
||||||
} else {
|
} else {
|
||||||
|
@ -98,15 +94,26 @@ static void bdi_queue_work(struct backing_dev_info *bdi,
|
||||||
* The bdi thread isn't there, wake up the forker thread which
|
* The bdi thread isn't there, wake up the forker thread which
|
||||||
* will create and run it.
|
* will create and run it.
|
||||||
*/
|
*/
|
||||||
trace_writeback_nothread(bdi, work);
|
|
||||||
wake_up_process(default_backing_dev_info.wb.task);
|
wake_up_process(default_backing_dev_info.wb.task);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bdi_queue_work(struct backing_dev_info *bdi,
|
||||||
|
struct wb_writeback_work *work)
|
||||||
|
{
|
||||||
|
trace_writeback_queue(bdi, work);
|
||||||
|
|
||||||
|
spin_lock_bh(&bdi->wb_lock);
|
||||||
|
list_add_tail(&work->list, &bdi->work_list);
|
||||||
|
if (!bdi->wb.task)
|
||||||
|
trace_writeback_nothread(bdi, work);
|
||||||
|
bdi_wakeup_flusher(bdi);
|
||||||
spin_unlock_bh(&bdi->wb_lock);
|
spin_unlock_bh(&bdi->wb_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
__bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
|
__bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
|
||||||
bool range_cyclic, bool for_background)
|
bool range_cyclic)
|
||||||
{
|
{
|
||||||
struct wb_writeback_work *work;
|
struct wb_writeback_work *work;
|
||||||
|
|
||||||
|
@ -126,7 +133,6 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
|
||||||
work->sync_mode = WB_SYNC_NONE;
|
work->sync_mode = WB_SYNC_NONE;
|
||||||
work->nr_pages = nr_pages;
|
work->nr_pages = nr_pages;
|
||||||
work->range_cyclic = range_cyclic;
|
work->range_cyclic = range_cyclic;
|
||||||
work->for_background = for_background;
|
|
||||||
|
|
||||||
bdi_queue_work(bdi, work);
|
bdi_queue_work(bdi, work);
|
||||||
}
|
}
|
||||||
|
@ -144,7 +150,7 @@ __bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages,
|
||||||
*/
|
*/
|
||||||
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages)
|
void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages)
|
||||||
{
|
{
|
||||||
__bdi_start_writeback(bdi, nr_pages, true, false);
|
__bdi_start_writeback(bdi, nr_pages, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -152,13 +158,21 @@ void bdi_start_writeback(struct backing_dev_info *bdi, long nr_pages)
|
||||||
* @bdi: the backing device to write from
|
* @bdi: the backing device to write from
|
||||||
*
|
*
|
||||||
* Description:
|
* Description:
|
||||||
* This does WB_SYNC_NONE background writeback. The IO is only
|
* This makes sure WB_SYNC_NONE background writeback happens. When
|
||||||
* started when this function returns, we make no guarentees on
|
* this function returns, it is only guaranteed that for given BDI
|
||||||
* completion. Caller need not hold sb s_umount semaphore.
|
* some IO is happening if we are over background dirty threshold.
|
||||||
|
* Caller need not hold sb s_umount semaphore.
|
||||||
*/
|
*/
|
||||||
void bdi_start_background_writeback(struct backing_dev_info *bdi)
|
void bdi_start_background_writeback(struct backing_dev_info *bdi)
|
||||||
{
|
{
|
||||||
__bdi_start_writeback(bdi, LONG_MAX, true, true);
|
/*
|
||||||
|
* We just wake up the flusher thread. It will perform background
|
||||||
|
* writeback as soon as there is no other work to do.
|
||||||
|
*/
|
||||||
|
trace_writeback_wake_background(bdi);
|
||||||
|
spin_lock_bh(&bdi->wb_lock);
|
||||||
|
bdi_wakeup_flusher(bdi);
|
||||||
|
spin_unlock_bh(&bdi->wb_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -616,6 +630,7 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
};
|
};
|
||||||
unsigned long oldest_jif;
|
unsigned long oldest_jif;
|
||||||
long wrote = 0;
|
long wrote = 0;
|
||||||
|
long write_chunk;
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
|
|
||||||
if (wbc.for_kupdate) {
|
if (wbc.for_kupdate) {
|
||||||
|
@ -628,6 +643,24 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
wbc.range_end = LLONG_MAX;
|
wbc.range_end = LLONG_MAX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* WB_SYNC_ALL mode does livelock avoidance by syncing dirty
|
||||||
|
* inodes/pages in one big loop. Setting wbc.nr_to_write=LONG_MAX
|
||||||
|
* here avoids calling into writeback_inodes_wb() more than once.
|
||||||
|
*
|
||||||
|
* The intended call sequence for WB_SYNC_ALL writeback is:
|
||||||
|
*
|
||||||
|
* wb_writeback()
|
||||||
|
* __writeback_inodes_sb() <== called only once
|
||||||
|
* write_cache_pages() <== called once for each inode
|
||||||
|
* (quickly) tag currently dirty pages
|
||||||
|
* (maybe slowly) sync all tagged pages
|
||||||
|
*/
|
||||||
|
if (wbc.sync_mode == WB_SYNC_NONE)
|
||||||
|
write_chunk = MAX_WRITEBACK_PAGES;
|
||||||
|
else
|
||||||
|
write_chunk = LONG_MAX;
|
||||||
|
|
||||||
wbc.wb_start = jiffies; /* livelock avoidance */
|
wbc.wb_start = jiffies; /* livelock avoidance */
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/*
|
/*
|
||||||
|
@ -636,6 +669,16 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
if (work->nr_pages <= 0)
|
if (work->nr_pages <= 0)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Background writeout and kupdate-style writeback may
|
||||||
|
* run forever. Stop them if there is other work to do
|
||||||
|
* so that e.g. sync can proceed. They'll be restarted
|
||||||
|
* after the other works are all done.
|
||||||
|
*/
|
||||||
|
if ((work->for_background || work->for_kupdate) &&
|
||||||
|
!list_empty(&wb->bdi->work_list))
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For background writeout, stop when we are below the
|
* For background writeout, stop when we are below the
|
||||||
* background dirty threshold
|
* background dirty threshold
|
||||||
|
@ -644,7 +687,7 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
wbc.more_io = 0;
|
wbc.more_io = 0;
|
||||||
wbc.nr_to_write = MAX_WRITEBACK_PAGES;
|
wbc.nr_to_write = write_chunk;
|
||||||
wbc.pages_skipped = 0;
|
wbc.pages_skipped = 0;
|
||||||
|
|
||||||
trace_wbc_writeback_start(&wbc, wb->bdi);
|
trace_wbc_writeback_start(&wbc, wb->bdi);
|
||||||
|
@ -654,8 +697,8 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
writeback_inodes_wb(wb, &wbc);
|
writeback_inodes_wb(wb, &wbc);
|
||||||
trace_wbc_writeback_written(&wbc, wb->bdi);
|
trace_wbc_writeback_written(&wbc, wb->bdi);
|
||||||
|
|
||||||
work->nr_pages -= MAX_WRITEBACK_PAGES - wbc.nr_to_write;
|
work->nr_pages -= write_chunk - wbc.nr_to_write;
|
||||||
wrote += MAX_WRITEBACK_PAGES - wbc.nr_to_write;
|
wrote += write_chunk - wbc.nr_to_write;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we consumed everything, see if we have more
|
* If we consumed everything, see if we have more
|
||||||
|
@ -670,7 +713,7 @@ static long wb_writeback(struct bdi_writeback *wb,
|
||||||
/*
|
/*
|
||||||
* Did we write something? Try for more
|
* Did we write something? Try for more
|
||||||
*/
|
*/
|
||||||
if (wbc.nr_to_write < MAX_WRITEBACK_PAGES)
|
if (wbc.nr_to_write < write_chunk)
|
||||||
continue;
|
continue;
|
||||||
/*
|
/*
|
||||||
* Nothing written. Wait for some inode to
|
* Nothing written. Wait for some inode to
|
||||||
|
@ -718,6 +761,23 @@ static unsigned long get_nr_dirty_pages(void)
|
||||||
get_nr_dirty_inodes();
|
get_nr_dirty_inodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static long wb_check_background_flush(struct bdi_writeback *wb)
|
||||||
|
{
|
||||||
|
if (over_bground_thresh()) {
|
||||||
|
|
||||||
|
struct wb_writeback_work work = {
|
||||||
|
.nr_pages = LONG_MAX,
|
||||||
|
.sync_mode = WB_SYNC_NONE,
|
||||||
|
.for_background = 1,
|
||||||
|
.range_cyclic = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
return wb_writeback(wb, &work);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static long wb_check_old_data_flush(struct bdi_writeback *wb)
|
static long wb_check_old_data_flush(struct bdi_writeback *wb)
|
||||||
{
|
{
|
||||||
unsigned long expired;
|
unsigned long expired;
|
||||||
|
@ -787,6 +847,7 @@ long wb_do_writeback(struct bdi_writeback *wb, int force_wait)
|
||||||
* Check for periodic writeback, kupdated() style
|
* Check for periodic writeback, kupdated() style
|
||||||
*/
|
*/
|
||||||
wrote += wb_check_old_data_flush(wb);
|
wrote += wb_check_old_data_flush(wb);
|
||||||
|
wrote += wb_check_background_flush(wb);
|
||||||
clear_bit(BDI_writeback_running, &wb->bdi->state);
|
clear_bit(BDI_writeback_running, &wb->bdi->state);
|
||||||
|
|
||||||
return wrote;
|
return wrote;
|
||||||
|
@ -873,7 +934,7 @@ void wakeup_flusher_threads(long nr_pages)
|
||||||
list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
|
list_for_each_entry_rcu(bdi, &bdi_list, bdi_list) {
|
||||||
if (!bdi_has_dirty_io(bdi))
|
if (!bdi_has_dirty_io(bdi))
|
||||||
continue;
|
continue;
|
||||||
__bdi_start_writeback(bdi, nr_pages, false, false);
|
__bdi_start_writeback(bdi, nr_pages, false);
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
}
|
}
|
||||||
|
@ -1164,7 +1225,7 @@ EXPORT_SYMBOL(writeback_inodes_sb_nr_if_idle);
|
||||||
* @sb: the superblock
|
* @sb: the superblock
|
||||||
*
|
*
|
||||||
* This function writes and waits on any dirty inode belonging to this
|
* This function writes and waits on any dirty inode belonging to this
|
||||||
* super_block. The number of pages synced is returned.
|
* super_block.
|
||||||
*/
|
*/
|
||||||
void sync_inodes_sb(struct super_block *sb)
|
void sync_inodes_sb(struct super_block *sb)
|
||||||
{
|
{
|
||||||
|
@ -1242,11 +1303,11 @@ int sync_inode(struct inode *inode, struct writeback_control *wbc)
|
||||||
EXPORT_SYMBOL(sync_inode);
|
EXPORT_SYMBOL(sync_inode);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* sync_inode - write an inode to disk
|
* sync_inode_metadata - write an inode to disk
|
||||||
* @inode: the inode to sync
|
* @inode: the inode to sync
|
||||||
* @wait: wait for I/O to complete.
|
* @wait: wait for I/O to complete.
|
||||||
*
|
*
|
||||||
* Write an inode to disk and adjust it's dirty state after completion.
|
* Write an inode to disk and adjust its dirty state after completion.
|
||||||
*
|
*
|
||||||
* Note: only writes the actual inode, no associated data or other metadata.
|
* Note: only writes the actual inode, no associated data or other metadata.
|
||||||
*/
|
*/
|
||||||
|
|
49
fs/mpage.c
49
fs/mpage.c
|
@ -40,7 +40,7 @@
|
||||||
* status of that page is hard. See end_buffer_async_read() for the details.
|
* status of that page is hard. See end_buffer_async_read() for the details.
|
||||||
* There is no point in duplicating all that complexity.
|
* There is no point in duplicating all that complexity.
|
||||||
*/
|
*/
|
||||||
static void mpage_end_io_read(struct bio *bio, int err)
|
static void mpage_end_io(struct bio *bio, int err)
|
||||||
{
|
{
|
||||||
const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
||||||
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
|
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
|
||||||
|
@ -50,44 +50,29 @@ static void mpage_end_io_read(struct bio *bio, int err)
|
||||||
|
|
||||||
if (--bvec >= bio->bi_io_vec)
|
if (--bvec >= bio->bi_io_vec)
|
||||||
prefetchw(&bvec->bv_page->flags);
|
prefetchw(&bvec->bv_page->flags);
|
||||||
|
if (bio_data_dir(bio) == READ) {
|
||||||
if (uptodate) {
|
if (uptodate) {
|
||||||
SetPageUptodate(page);
|
SetPageUptodate(page);
|
||||||
} else {
|
} else {
|
||||||
ClearPageUptodate(page);
|
ClearPageUptodate(page);
|
||||||
SetPageError(page);
|
SetPageError(page);
|
||||||
|
}
|
||||||
|
unlock_page(page);
|
||||||
|
} else { /* bio_data_dir(bio) == WRITE */
|
||||||
|
if (!uptodate) {
|
||||||
|
SetPageError(page);
|
||||||
|
if (page->mapping)
|
||||||
|
set_bit(AS_EIO, &page->mapping->flags);
|
||||||
|
}
|
||||||
|
end_page_writeback(page);
|
||||||
}
|
}
|
||||||
unlock_page(page);
|
|
||||||
} while (bvec >= bio->bi_io_vec);
|
|
||||||
bio_put(bio);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mpage_end_io_write(struct bio *bio, int err)
|
|
||||||
{
|
|
||||||
const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
|
|
||||||
struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
|
|
||||||
|
|
||||||
do {
|
|
||||||
struct page *page = bvec->bv_page;
|
|
||||||
|
|
||||||
if (--bvec >= bio->bi_io_vec)
|
|
||||||
prefetchw(&bvec->bv_page->flags);
|
|
||||||
|
|
||||||
if (!uptodate){
|
|
||||||
SetPageError(page);
|
|
||||||
if (page->mapping)
|
|
||||||
set_bit(AS_EIO, &page->mapping->flags);
|
|
||||||
}
|
|
||||||
end_page_writeback(page);
|
|
||||||
} while (bvec >= bio->bi_io_vec);
|
} while (bvec >= bio->bi_io_vec);
|
||||||
bio_put(bio);
|
bio_put(bio);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct bio *mpage_bio_submit(int rw, struct bio *bio)
|
static struct bio *mpage_bio_submit(int rw, struct bio *bio)
|
||||||
{
|
{
|
||||||
bio->bi_end_io = mpage_end_io_read;
|
bio->bi_end_io = mpage_end_io;
|
||||||
if (rw == WRITE)
|
|
||||||
bio->bi_end_io = mpage_end_io_write;
|
|
||||||
submit_bio(rw, bio);
|
submit_bio(rw, bio);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1579,6 +1579,7 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||||
{
|
{
|
||||||
struct iattr attr;
|
struct iattr attr;
|
||||||
int error;
|
int error;
|
||||||
|
int open_flags = 0;
|
||||||
|
|
||||||
dfprintk(VFS, "NFS: create(%s/%ld), %s\n",
|
dfprintk(VFS, "NFS: create(%s/%ld), %s\n",
|
||||||
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
|
dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
|
||||||
|
@ -1586,7 +1587,10 @@ static int nfs_create(struct inode *dir, struct dentry *dentry, int mode,
|
||||||
attr.ia_mode = mode;
|
attr.ia_mode = mode;
|
||||||
attr.ia_valid = ATTR_MODE;
|
attr.ia_valid = ATTR_MODE;
|
||||||
|
|
||||||
error = NFS_PROTO(dir)->create(dir, dentry, &attr, 0, NULL);
|
if ((nd->flags & LOOKUP_CREATE) != 0)
|
||||||
|
open_flags = nd->intent.open.flags;
|
||||||
|
|
||||||
|
error = NFS_PROTO(dir)->create(dir, dentry, &attr, open_flags, NULL);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
goto out_err;
|
goto out_err;
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1151,7 +1151,7 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
|
||||||
goto err_task_lock;
|
goto err_task_lock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (oom_score_adj < task->signal->oom_score_adj &&
|
if (oom_score_adj < task->signal->oom_score_adj_min &&
|
||||||
!capable(CAP_SYS_RESOURCE)) {
|
!capable(CAP_SYS_RESOURCE)) {
|
||||||
err = -EACCES;
|
err = -EACCES;
|
||||||
goto err_sighand;
|
goto err_sighand;
|
||||||
|
@ -1164,6 +1164,8 @@ static ssize_t oom_score_adj_write(struct file *file, const char __user *buf,
|
||||||
atomic_dec(&task->mm->oom_disable_count);
|
atomic_dec(&task->mm->oom_disable_count);
|
||||||
}
|
}
|
||||||
task->signal->oom_score_adj = oom_score_adj;
|
task->signal->oom_score_adj = oom_score_adj;
|
||||||
|
if (has_capability_noaudit(current, CAP_SYS_RESOURCE))
|
||||||
|
task->signal->oom_score_adj_min = oom_score_adj;
|
||||||
/*
|
/*
|
||||||
* Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is
|
* Scale /proc/pid/oom_adj appropriately ensuring that OOM_DISABLE is
|
||||||
* always attainable.
|
* always attainable.
|
||||||
|
|
|
@ -100,6 +100,9 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
|
||||||
"VmallocChunk: %8lu kB\n"
|
"VmallocChunk: %8lu kB\n"
|
||||||
#ifdef CONFIG_MEMORY_FAILURE
|
#ifdef CONFIG_MEMORY_FAILURE
|
||||||
"HardwareCorrupted: %5lu kB\n"
|
"HardwareCorrupted: %5lu kB\n"
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
"AnonHugePages: %8lu kB\n"
|
||||||
#endif
|
#endif
|
||||||
,
|
,
|
||||||
K(i.totalram),
|
K(i.totalram),
|
||||||
|
@ -128,7 +131,12 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
|
||||||
K(i.freeswap),
|
K(i.freeswap),
|
||||||
K(global_page_state(NR_FILE_DIRTY)),
|
K(global_page_state(NR_FILE_DIRTY)),
|
||||||
K(global_page_state(NR_WRITEBACK)),
|
K(global_page_state(NR_WRITEBACK)),
|
||||||
K(global_page_state(NR_ANON_PAGES)),
|
K(global_page_state(NR_ANON_PAGES)
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
+ global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
|
||||||
|
HPAGE_PMD_NR
|
||||||
|
#endif
|
||||||
|
),
|
||||||
K(global_page_state(NR_FILE_MAPPED)),
|
K(global_page_state(NR_FILE_MAPPED)),
|
||||||
K(global_page_state(NR_SHMEM)),
|
K(global_page_state(NR_SHMEM)),
|
||||||
K(global_page_state(NR_SLAB_RECLAIMABLE) +
|
K(global_page_state(NR_SLAB_RECLAIMABLE) +
|
||||||
|
@ -150,6 +158,10 @@ static int meminfo_proc_show(struct seq_file *m, void *v)
|
||||||
vmi.largest_chunk >> 10
|
vmi.largest_chunk >> 10
|
||||||
#ifdef CONFIG_MEMORY_FAILURE
|
#ifdef CONFIG_MEMORY_FAILURE
|
||||||
,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10)
|
,atomic_long_read(&mce_bad_pages) << (PAGE_SHIFT - 10)
|
||||||
|
#endif
|
||||||
|
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
|
||||||
|
,K(global_page_state(NR_ANON_TRANSPARENT_HUGEPAGES) *
|
||||||
|
HPAGE_PMD_NR)
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue