diff --git a/[refs] b/[refs] index 38659fc26448..48bd38792d81 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: af487e4209ef5e82b1932b8b15fd59efbd56a955 +refs/heads/master: febd41d59dc4a8125b1b5fd0b947ab795bfe4aa5 diff --git a/trunk/Documentation/ABI/testing/sysfs-bus-rbd b/trunk/Documentation/ABI/testing/sysfs-bus-rbd index cd9213ccf3dc..1cf2adf46b11 100644 --- a/trunk/Documentation/ABI/testing/sysfs-bus-rbd +++ b/trunk/Documentation/ABI/testing/sysfs-bus-rbd @@ -70,10 +70,6 @@ snap_* A directory per each snapshot -parent - - Information identifying the pool, image, and snapshot id for - the parent image in a layered rbd image (format 2 only). Entries under /sys/bus/rbd/devices//snap_ ------------------------------------------------------------- diff --git a/trunk/Documentation/DMA-API-HOWTO.txt b/trunk/Documentation/DMA-API-HOWTO.txt index 4a4fb295ceef..a0b6250add79 100644 --- a/trunk/Documentation/DMA-API-HOWTO.txt +++ b/trunk/Documentation/DMA-API-HOWTO.txt @@ -468,46 +468,11 @@ To map a single region, you do: size_t size = buffer->len; dma_handle = dma_map_single(dev, addr, size, direction); - if (dma_mapping_error(dma_handle)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling; - } and to unmap it: dma_unmap_single(dev, dma_handle, size, direction); -You should call dma_mapping_error() as dma_map_single() could fail and return -error. Not all dma implementations support dma_mapping_error() interface. -However, it is a good practice to call dma_mapping_error() interface, which -will invoke the generic mapping error check interface. Doing so will ensure -that the mapping code will work correctly on all dma implementations without -any dependency on the specifics of the underlying implementation. Using the -returned address without checking for errors could result in failures ranging -from panics to silent data corruption. Couple of example of incorrect ways to -check for errors that make assumptions about the underlying dma implementation -are as follows and these are applicable to dma_map_page() as well. - -Incorrect example 1: - dma_addr_t dma_handle; - - dma_handle = dma_map_single(dev, addr, size, direction); - if ((dma_handle & 0xffff != 0) || (dma_handle >= 0x1000000)) { - goto map_error; - } - -Incorrect example 2: - dma_addr_t dma_handle; - - dma_handle = dma_map_single(dev, addr, size, direction); - if (dma_handle == DMA_ERROR_CODE) { - goto map_error; - } - You should call dma_unmap_single when the DMA activity is finished, e.g. from the interrupt which told you that the DMA transfer is done. @@ -524,14 +489,6 @@ Specifically: size_t size = buffer->len; dma_handle = dma_map_page(dev, page, offset, size, direction); - if (dma_mapping_error(dma_handle)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling; - } ... @@ -539,12 +496,6 @@ Specifically: Here, "offset" means byte offset within the given page. -You should call dma_mapping_error() as dma_map_page() could fail and return -error as outlined under the dma_map_single() discussion. - -You should call dma_unmap_page when the DMA activity is finished, e.g. -from the interrupt which told you that the DMA transfer is done. - With scatterlists, you map a region gathered from several regions by: int i, count = dma_map_sg(dev, sglist, nents, direction); @@ -627,14 +578,6 @@ to use the dma_sync_*() interfaces. dma_addr_t mapping; mapping = dma_map_single(cp->dev, buffer, len, DMA_FROM_DEVICE); - if (dma_mapping_error(dma_handle)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling; - } cp->rx_buf = buffer; cp->rx_len = len; @@ -715,75 +658,6 @@ failure can be determined by: * delay and try again later or * reset driver. */ - goto map_error_handling; - } - -- unmap pages that are already mapped, when mapping error occurs in the middle - of a multiple page mapping attempt. These example are applicable to - dma_map_page() as well. - -Example 1: - dma_addr_t dma_handle1; - dma_addr_t dma_handle2; - - dma_handle1 = dma_map_single(dev, addr, size, direction); - if (dma_mapping_error(dev, dma_handle1)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling1; - } - dma_handle2 = dma_map_single(dev, addr, size, direction); - if (dma_mapping_error(dev, dma_handle2)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling2; - } - - ... - - map_error_handling2: - dma_unmap_single(dma_handle1); - map_error_handling1: - -Example 2: (if buffers are allocated a loop, unmap all mapped buffers when - mapping error is detected in the middle) - - dma_addr_t dma_addr; - dma_addr_t array[DMA_BUFFERS]; - int save_index = 0; - - for (i = 0; i < DMA_BUFFERS; i++) { - - ... - - dma_addr = dma_map_single(dev, addr, size, direction); - if (dma_mapping_error(dev, dma_addr)) { - /* - * reduce current DMA mapping usage, - * delay and try again later or - * reset driver. - */ - goto map_error_handling; - } - array[i].dma_addr = dma_addr; - save_index++; - } - - ... - - map_error_handling: - - for (i = 0; i < save_index; i++) { - - ... - - dma_unmap_single(array[i].dma_addr); } Networking drivers must call dev_kfree_skb to free the socket buffer diff --git a/trunk/Documentation/DMA-API.txt b/trunk/Documentation/DMA-API.txt index 78a6c569d204..66bd97a95f10 100644 --- a/trunk/Documentation/DMA-API.txt +++ b/trunk/Documentation/DMA-API.txt @@ -678,15 +678,3 @@ out of dma_debug_entries. These entries are preallocated at boot. The number of preallocated entries is defined per architecture. If it is too low for you boot with 'dma_debug_entries=' to overwrite the architectural default. - -void debug_dmap_mapping_error(struct device *dev, dma_addr_t dma_addr); - -dma-debug interface debug_dma_mapping_error() to debug drivers that fail -to check dma mapping errors on addresses returned by dma_map_single() and -dma_map_page() interfaces. This interface clears a flag set by -debug_dma_map_page() to indicate that dma_mapping_error() has been called by -the driver. When driver does unmap, debug_dma_unmap() checks the flag and if -this flag is still set, prints warning message that includes call trace that -leads up to the unmap. This interface can be called from dma_mapping_error() -routines to enable dma mapping error check debugging. - diff --git a/trunk/Documentation/filesystems/00-INDEX b/trunk/Documentation/filesystems/00-INDEX index 8042050eb265..7b52ba7bf32a 100644 --- a/trunk/Documentation/filesystems/00-INDEX +++ b/trunk/Documentation/filesystems/00-INDEX @@ -50,8 +50,6 @@ ext4.txt - info, mount options and specifications for the Ext4 filesystem. files.txt - info on file management in the Linux kernel. -f2fs.txt - - info and mount options for the F2FS filesystem. fuse.txt - info on the Filesystem in User SpacE including mount options. gfs2.txt diff --git a/trunk/Documentation/filesystems/f2fs.txt b/trunk/Documentation/filesystems/f2fs.txt deleted file mode 100644 index 8fbd8b46ee34..000000000000 --- a/trunk/Documentation/filesystems/f2fs.txt +++ /dev/null @@ -1,421 +0,0 @@ -================================================================================ -WHAT IS Flash-Friendly File System (F2FS)? -================================================================================ - -NAND flash memory-based storage devices, such as SSD, eMMC, and SD cards, have -been equipped on a variety systems ranging from mobile to server systems. Since -they are known to have different characteristics from the conventional rotating -disks, a file system, an upper layer to the storage device, should adapt to the -changes from the sketch in the design level. - -F2FS is a file system exploiting NAND flash memory-based storage devices, which -is based on Log-structured File System (LFS). The design has been focused on -addressing the fundamental issues in LFS, which are snowball effect of wandering -tree and high cleaning overhead. - -Since a NAND flash memory-based storage device shows different characteristic -according to its internal geometry or flash memory management scheme, namely FTL, -F2FS and its tools support various parameters not only for configuring on-disk -layout, but also for selecting allocation and cleaning algorithms. - -The file system formatting tool, "mkfs.f2fs", is available from the following -git tree: ->> git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git - -For reporting bugs and sending patches, please use the following mailing list: ->> linux-f2fs-devel@lists.sourceforge.net - -================================================================================ -BACKGROUND AND DESIGN ISSUES -================================================================================ - -Log-structured File System (LFS) --------------------------------- -"A log-structured file system writes all modifications to disk sequentially in -a log-like structure, thereby speeding up both file writing and crash recovery. -The log is the only structure on disk; it contains indexing information so that -files can be read back from the log efficiently. In order to maintain large free -areas on disk for fast writing, we divide the log into segments and use a -segment cleaner to compress the live information from heavily fragmented -segments." from Rosenblum, M. and Ousterhout, J. K., 1992, "The design and -implementation of a log-structured file system", ACM Trans. Computer Systems -10, 1, 26–52. - -Wandering Tree Problem ----------------------- -In LFS, when a file data is updated and written to the end of log, its direct -pointer block is updated due to the changed location. Then the indirect pointer -block is also updated due to the direct pointer block update. In this manner, -the upper index structures such as inode, inode map, and checkpoint block are -also updated recursively. This problem is called as wandering tree problem [1], -and in order to enhance the performance, it should eliminate or relax the update -propagation as much as possible. - -[1] Bityutskiy, A. 2005. JFFS3 design issues. http://www.linux-mtd.infradead.org/ - -Cleaning Overhead ------------------ -Since LFS is based on out-of-place writes, it produces so many obsolete blocks -scattered across the whole storage. In order to serve new empty log space, it -needs to reclaim these obsolete blocks seamlessly to users. This job is called -as a cleaning process. - -The process consists of three operations as follows. -1. A victim segment is selected through referencing segment usage table. -2. It loads parent index structures of all the data in the victim identified by - segment summary blocks. -3. It checks the cross-reference between the data and its parent index structure. -4. It moves valid data selectively. - -This cleaning job may cause unexpected long delays, so the most important goal -is to hide the latencies to users. And also definitely, it should reduce the -amount of valid data to be moved, and move them quickly as well. - -================================================================================ -KEY FEATURES -================================================================================ - -Flash Awareness ---------------- -- Enlarge the random write area for better performance, but provide the high - spatial locality -- Align FS data structures to the operational units in FTL as best efforts - -Wandering Tree Problem ----------------------- -- Use a term, “node”, that represents inodes as well as various pointer blocks -- Introduce Node Address Table (NAT) containing the locations of all the “node” - blocks; this will cut off the update propagation. - -Cleaning Overhead ------------------ -- Support a background cleaning process -- Support greedy and cost-benefit algorithms for victim selection policies -- Support multi-head logs for static/dynamic hot and cold data separation -- Introduce adaptive logging for efficient block allocation - -================================================================================ -MOUNT OPTIONS -================================================================================ - -background_gc_off Turn off cleaning operations, namely garbage collection, - triggered in background when I/O subsystem is idle. -disable_roll_forward Disable the roll-forward recovery routine -discard Issue discard/TRIM commands when a segment is cleaned. -no_heap Disable heap-style segment allocation which finds free - segments for data from the beginning of main area, while - for node from the end of main area. -nouser_xattr Disable Extended User Attributes. Note: xattr is enabled - by default if CONFIG_F2FS_FS_XATTR is selected. -noacl Disable POSIX Access Control List. Note: acl is enabled - by default if CONFIG_F2FS_FS_POSIX_ACL is selected. -active_logs=%u Support configuring the number of active logs. In the - current design, f2fs supports only 2, 4, and 6 logs. - Default number is 6. -disable_ext_identify Disable the extension list configured by mkfs, so f2fs - does not aware of cold files such as media files. - -================================================================================ -DEBUGFS ENTRIES -================================================================================ - -/sys/kernel/debug/f2fs/ contains information about all the partitions mounted as -f2fs. Each file shows the whole f2fs information. - -/sys/kernel/debug/f2fs/status includes: - - major file system information managed by f2fs currently - - average SIT information about whole segments - - current memory footprint consumed by f2fs. - -================================================================================ -USAGE -================================================================================ - -1. Download userland tools and compile them. - -2. Skip, if f2fs was compiled statically inside kernel. - Otherwise, insert the f2fs.ko module. - # insmod f2fs.ko - -3. Create a directory trying to mount - # mkdir /mnt/f2fs - -4. Format the block device, and then mount as f2fs - # mkfs.f2fs -l label /dev/block_device - # mount -t f2fs /dev/block_device /mnt/f2fs - -Format options --------------- --l [label] : Give a volume label, up to 256 unicode name. --a [0 or 1] : Split start location of each area for heap-based allocation. - 1 is set by default, which performs this. --o [int] : Set overprovision ratio in percent over volume size. - 5 is set by default. --s [int] : Set the number of segments per section. - 1 is set by default. --z [int] : Set the number of sections per zone. - 1 is set by default. --e [str] : Set basic extension list. e.g. "mp3,gif,mov" - -================================================================================ -DESIGN -================================================================================ - -On-disk Layout --------------- - -F2FS divides the whole volume into a number of segments, each of which is fixed -to 2MB in size. A section is composed of consecutive segments, and a zone -consists of a set of sections. By default, section and zone sizes are set to one -segment size identically, but users can easily modify the sizes by mkfs. - -F2FS splits the entire volume into six areas, and all the areas except superblock -consists of multiple segments as described below. - - align with the zone size <-| - |-> align with the segment size - _________________________________________________________________________ - | | | Node | Segment | Segment | | - | Superblock | Checkpoint | Address | Info. | Summary | Main | - | (SB) | (CP) | Table (NAT) | Table (SIT) | Area (SSA) | | - |____________|_____2______|______N______|______N______|______N_____|__N___| - . . - . . - . . - ._________________________________________. - |_Segment_|_..._|_Segment_|_..._|_Segment_| - . . - ._________._________ - |_section_|__...__|_ - . . - .________. - |__zone__| - -- Superblock (SB) - : It is located at the beginning of the partition, and there exist two copies - to avoid file system crash. It contains basic partition information and some - default parameters of f2fs. - -- Checkpoint (CP) - : It contains file system information, bitmaps for valid NAT/SIT sets, orphan - inode lists, and summary entries of current active segments. - -- Node Address Table (NAT) - : It is composed of a block address table for all the node blocks stored in - Main area. - -- Segment Information Table (SIT) - : It contains segment information such as valid block count and bitmap for the - validity of all the blocks. - -- Segment Summary Area (SSA) - : It contains summary entries which contains the owner information of all the - data and node blocks stored in Main area. - -- Main Area - : It contains file and directory data including their indices. - -In order to avoid misalignment between file system and flash-based storage, F2FS -aligns the start block address of CP with the segment size. Also, it aligns the -start block address of Main area with the zone size by reserving some segments -in SSA area. - -Reference the following survey for additional technical details. -https://wiki.linaro.org/WorkingGroups/Kernel/Projects/FlashCardSurvey - -File System Metadata Structure ------------------------------- - -F2FS adopts the checkpointing scheme to maintain file system consistency. At -mount time, F2FS first tries to find the last valid checkpoint data by scanning -CP area. In order to reduce the scanning time, F2FS uses only two copies of CP. -One of them always indicates the last valid data, which is called as shadow copy -mechanism. In addition to CP, NAT and SIT also adopt the shadow copy mechanism. - -For file system consistency, each CP points to which NAT and SIT copies are -valid, as shown as below. - - +--------+----------+---------+ - | CP | NAT | SIT | - +--------+----------+---------+ - . . . . - . . . . - . . . . - +-------+-------+--------+--------+--------+--------+ - | CP #0 | CP #1 | NAT #0 | NAT #1 | SIT #0 | SIT #1 | - +-------+-------+--------+--------+--------+--------+ - | ^ ^ - | | | - `----------------------------------------' - -Index Structure ---------------- - -The key data structure to manage the data locations is a "node". Similar to -traditional file structures, F2FS has three types of node: inode, direct node, -indirect node. F2FS assigns 4KB to an inode block which contains 923 data block -indices, two direct node pointers, two indirect node pointers, and one double -indirect node pointer as described below. One direct node block contains 1018 -data blocks, and one indirect node block contains also 1018 node blocks. Thus, -one inode block (i.e., a file) covers: - - 4KB * (923 + 2 * 1018 + 2 * 1018 * 1018 + 1018 * 1018 * 1018) := 3.94TB. - - Inode block (4KB) - |- data (923) - |- direct node (2) - | `- data (1018) - |- indirect node (2) - | `- direct node (1018) - | `- data (1018) - `- double indirect node (1) - `- indirect node (1018) - `- direct node (1018) - `- data (1018) - -Note that, all the node blocks are mapped by NAT which means the location of -each node is translated by the NAT table. In the consideration of the wandering -tree problem, F2FS is able to cut off the propagation of node updates caused by -leaf data writes. - -Directory Structure -------------------- - -A directory entry occupies 11 bytes, which consists of the following attributes. - -- hash hash value of the file name -- ino inode number -- len the length of file name -- type file type such as directory, symlink, etc - -A dentry block consists of 214 dentry slots and file names. Therein a bitmap is -used to represent whether each dentry is valid or not. A dentry block occupies -4KB with the following composition. - - Dentry Block(4 K) = bitmap (27 bytes) + reserved (3 bytes) + - dentries(11 * 214 bytes) + file name (8 * 214 bytes) - - [Bucket] - +--------------------------------+ - |dentry block 1 | dentry block 2 | - +--------------------------------+ - . . - . . - . [Dentry Block Structure: 4KB] . - +--------+----------+----------+------------+ - | bitmap | reserved | dentries | file names | - +--------+----------+----------+------------+ - [Dentry Block: 4KB] . . - . . - . . - +------+------+-----+------+ - | hash | ino | len | type | - +------+------+-----+------+ - [Dentry Structure: 11 bytes] - -F2FS implements multi-level hash tables for directory structure. Each level has -a hash table with dedicated number of hash buckets as shown below. Note that -"A(2B)" means a bucket includes 2 data blocks. - ----------------------- -A : bucket -B : block -N : MAX_DIR_HASH_DEPTH ----------------------- - -level #0 | A(2B) - | -level #1 | A(2B) - A(2B) - | -level #2 | A(2B) - A(2B) - A(2B) - A(2B) - . | . . . . -level #N/2 | A(2B) - A(2B) - A(2B) - A(2B) - A(2B) - ... - A(2B) - . | . . . . -level #N | A(4B) - A(4B) - A(4B) - A(4B) - A(4B) - ... - A(4B) - -The number of blocks and buckets are determined by, - - ,- 2, if n < MAX_DIR_HASH_DEPTH / 2, - # of blocks in level #n = | - `- 4, Otherwise - - ,- 2^n, if n < MAX_DIR_HASH_DEPTH / 2, - # of buckets in level #n = | - `- 2^((MAX_DIR_HASH_DEPTH / 2) - 1), Otherwise - -When F2FS finds a file name in a directory, at first a hash value of the file -name is calculated. Then, F2FS scans the hash table in level #0 to find the -dentry consisting of the file name and its inode number. If not found, F2FS -scans the next hash table in level #1. In this way, F2FS scans hash tables in -each levels incrementally from 1 to N. In each levels F2FS needs to scan only -one bucket determined by the following equation, which shows O(log(# of files)) -complexity. - - bucket number to scan in level #n = (hash value) % (# of buckets in level #n) - -In the case of file creation, F2FS finds empty consecutive slots that cover the -file name. F2FS searches the empty slots in the hash tables of whole levels from -1 to N in the same way as the lookup operation. - -The following figure shows an example of two cases holding children. - --------------> Dir <-------------- - | | - child child - - child - child [hole] - child - - child - child - child [hole] - [hole] - child - - Case 1: Case 2: - Number of children = 6, Number of children = 3, - File size = 7 File size = 7 - -Default Block Allocation ------------------------- - -At runtime, F2FS manages six active logs inside "Main" area: Hot/Warm/Cold node -and Hot/Warm/Cold data. - -- Hot node contains direct node blocks of directories. -- Warm node contains direct node blocks except hot node blocks. -- Cold node contains indirect node blocks -- Hot data contains dentry blocks -- Warm data contains data blocks except hot and cold data blocks -- Cold data contains multimedia data or migrated data blocks - -LFS has two schemes for free space management: threaded log and copy-and-compac- -tion. The copy-and-compaction scheme which is known as cleaning, is well-suited -for devices showing very good sequential write performance, since free segments -are served all the time for writing new data. However, it suffers from cleaning -overhead under high utilization. Contrarily, the threaded log scheme suffers -from random writes, but no cleaning process is needed. F2FS adopts a hybrid -scheme where the copy-and-compaction scheme is adopted by default, but the -policy is dynamically changed to the threaded log scheme according to the file -system status. - -In order to align F2FS with underlying flash-based storage, F2FS allocates a -segment in a unit of section. F2FS expects that the section size would be the -same as the unit size of garbage collection in FTL. Furthermore, with respect -to the mapping granularity in FTL, F2FS allocates each section of the active -logs from different zones as much as possible, since FTL can write the data in -the active logs into one allocation unit according to its mapping granularity. - -Cleaning process ----------------- - -F2FS does cleaning both on demand and in the background. On-demand cleaning is -triggered when there are not enough free segments to serve VFS calls. Background -cleaner is operated by a kernel thread, and triggers the cleaning job when the -system is idle. - -F2FS supports two victim selection policies: greedy and cost-benefit algorithms. -In the greedy algorithm, F2FS selects a victim segment having the smallest number -of valid blocks. In the cost-benefit algorithm, F2FS selects a victim segment -according to the segment age and the number of valid blocks in order to address -log block thrashing problem in the greedy algorithm. F2FS adopts the greedy -algorithm for on-demand cleaner, while background cleaner adopts cost-benefit -algorithm. - -In order to identify whether the data in the victim segment are valid or not, -F2FS manages a bitmap. Each bit represents the validity of a block, and the -bitmap is composed of a bit stream covering whole blocks in main area. diff --git a/trunk/Documentation/filesystems/nfs/nfs41-server.txt b/trunk/Documentation/filesystems/nfs/nfs41-server.txt index 01c2db769791..092fad92a3f0 100644 --- a/trunk/Documentation/filesystems/nfs/nfs41-server.txt +++ b/trunk/Documentation/filesystems/nfs/nfs41-server.txt @@ -39,10 +39,21 @@ interoperability problems with future clients. Known issues: from a linux client are possible, but we aren't really conformant with the spec (for example, we don't use kerberos on the backchannel correctly). + - Incomplete backchannel support: incomplete backchannel gss + support and no support for BACKCHANNEL_CTL mean that + callbacks (hence delegations and layouts) may not be + available and clients confused by the incomplete + implementation may fail. - We do not support SSV, which provides security for shared client-server state (thus preventing unauthorized tampering with locks and opens, for example). It is mandatory for servers to support this, though no clients use it yet. + - Mandatory operations which we do not support, such as + DESTROY_CLIENTID, are not currently used by clients, but will be + (and the spec recommends their uses in common cases), and + clients should not be expected to know how to recover from the + case where they are not supported. This will eventually cause + interoperability failures. In addition, some limitations are inherited from the current NFSv4 implementation: @@ -78,7 +89,7 @@ Operations | | MNI | or OPT) | | +----------------------+------------+--------------+----------------+ | ACCESS | REQ | | Section 18.1 | -I | BACKCHANNEL_CTL | REQ | | Section 18.33 | +NS | BACKCHANNEL_CTL | REQ | | Section 18.33 | I | BIND_CONN_TO_SESSION | REQ | | Section 18.34 | | CLOSE | REQ | | Section 18.2 | | COMMIT | REQ | | Section 18.3 | @@ -88,7 +99,7 @@ NS*| DELEGPURGE | OPT | FDELG (REQ) | Section 18.5 | | DELEGRETURN | OPT | FDELG, | Section 18.6 | | | | DDELG, pNFS | | | | | (REQ) | | -I | DESTROY_CLIENTID | REQ | | Section 18.50 | +NS | DESTROY_CLIENTID | REQ | | Section 18.50 | I | DESTROY_SESSION | REQ | | Section 18.37 | I | EXCHANGE_ID | REQ | | Section 18.35 | I | FREE_STATEID | REQ | | Section 18.38 | @@ -181,6 +192,7 @@ EXCHANGE_ID: CREATE_SESSION: * backchannel attributes are ignored +* backchannel security parameters are ignored SEQUENCE: * no support for dynamic slot table renegotiation (optional) @@ -190,7 +202,7 @@ Nonstandard compound limitations: ca_maxrequestsize request and a ca_maxresponsesize reply, so we may fail to live up to the promise we made in CREATE_SESSION fore channel negotiation. -* No more than one read-like operation allowed per compound; encoding - replies that cross page boundaries (except for read data) not handled. +* No more than one IO operation (read, write, readdir) allowed per + compound. See also http://wiki.linux-nfs.org/wiki/index.php/Server_4.0_and_4.1_issues. diff --git a/trunk/Makefile b/trunk/Makefile index 4fe05595b2da..6f07f4a28b47 100644 --- a/trunk/Makefile +++ b/trunk/Makefile @@ -124,7 +124,7 @@ $(if $(KBUILD_OUTPUT),, \ PHONY += $(MAKECMDGOALS) sub-make $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make - @: + $(Q)@: sub-make: FORCE $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) \ @@ -1027,14 +1027,11 @@ clean: rm-dirs := $(CLEAN_DIRS) clean: rm-files := $(CLEAN_FILES) clean-dirs := $(addprefix _clean_, . $(vmlinux-alldirs) Documentation samples) -PHONY += $(clean-dirs) clean archclean vmlinuxclean +PHONY += $(clean-dirs) clean archclean $(clean-dirs): $(Q)$(MAKE) $(clean)=$(patsubst _clean_%,%,$@) -vmlinuxclean: - $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean - -clean: archclean vmlinuxclean +clean: archclean # mrproper - Delete all generated files, including .config # @@ -1261,6 +1258,7 @@ scripts: ; endif # KBUILD_EXTMOD clean: $(clean-dirs) + $(Q)$(CONFIG_SHELL) $(srctree)/scripts/link-vmlinux.sh clean $(call cmd,rmdirs) $(call cmd,rmfiles) @find $(if $(KBUILD_EXTMOD), $(KBUILD_EXTMOD), .) $(RCS_FIND_IGNORE) \ diff --git a/trunk/arch/arm/boot/dts/sun4i-cubieboard.dts b/trunk/arch/arm/boot/dts/sun4i-a10-cubieboard.dts similarity index 100% rename from trunk/arch/arm/boot/dts/sun4i-cubieboard.dts rename to trunk/arch/arm/boot/dts/sun4i-a10-cubieboard.dts diff --git a/trunk/arch/arm/boot/dts/sun4i.dtsi b/trunk/arch/arm/boot/dts/sun4i-a10.dtsi similarity index 100% rename from trunk/arch/arm/boot/dts/sun4i.dtsi rename to trunk/arch/arm/boot/dts/sun4i-a10.dtsi diff --git a/trunk/arch/arm/boot/dts/sun5i-olinuxino.dts b/trunk/arch/arm/boot/dts/sun5i-a13-olinuxino.dts similarity index 100% rename from trunk/arch/arm/boot/dts/sun5i-olinuxino.dts rename to trunk/arch/arm/boot/dts/sun5i-a13-olinuxino.dts diff --git a/trunk/arch/arm/boot/dts/sun5i.dtsi b/trunk/arch/arm/boot/dts/sun5i-a13.dtsi similarity index 100% rename from trunk/arch/arm/boot/dts/sun5i.dtsi rename to trunk/arch/arm/boot/dts/sun5i-a13.dtsi diff --git a/trunk/arch/arm/include/asm/dma-mapping.h b/trunk/arch/arm/include/asm/dma-mapping.h index 5b579b951503..67d06324e74a 100644 --- a/trunk/arch/arm/include/asm/dma-mapping.h +++ b/trunk/arch/arm/include/asm/dma-mapping.h @@ -91,7 +91,6 @@ static inline dma_addr_t virt_to_dma(struct device *dev, void *addr) */ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - debug_dma_mapping_error(dev, dma_addr); return dma_addr == DMA_ERROR_CODE; } diff --git a/trunk/arch/arm/mach-omap2/devices.c b/trunk/arch/arm/mach-omap2/devices.c index 5e304d0719a2..4abb8b5e9bc0 100644 --- a/trunk/arch/arm/mach-omap2/devices.c +++ b/trunk/arch/arm/mach-omap2/devices.c @@ -226,7 +226,7 @@ static struct platform_device omap3isp_device = { }; static struct omap_iommu_arch_data omap3_isp_iommu = { - .name = "mmu_isp", + .name = "isp", }; int omap3_init_camera(struct isp_platform_data *pdata) diff --git a/trunk/arch/arm/mach-omap2/omap-iommu.c b/trunk/arch/arm/mach-omap2/omap-iommu.c index 6da4f7ae9d7f..a6a4ff8744b7 100644 --- a/trunk/arch/arm/mach-omap2/omap-iommu.c +++ b/trunk/arch/arm/mach-omap2/omap-iommu.c @@ -12,60 +12,153 @@ #include #include -#include -#include #include -#include "omap_hwmod.h" -#include "omap_device.h" -static int __init omap_iommu_dev_init(struct omap_hwmod *oh, void *unused) +#include "soc.h" +#include "common.h" + +struct iommu_device { + resource_size_t base; + int irq; + struct iommu_platform_data pdata; + struct resource res[2]; +}; +static struct iommu_device *devices; +static int num_iommu_devices; + +#ifdef CONFIG_ARCH_OMAP3 +static struct iommu_device omap3_devices[] = { + { + .base = 0x480bd400, + .irq = 24 + OMAP_INTC_START, + .pdata = { + .name = "isp", + .nr_tlb_entries = 8, + .clk_name = "cam_ick", + .da_start = 0x0, + .da_end = 0xFFFFF000, + }, + }, +#if defined(CONFIG_OMAP_IOMMU_IVA2) + { + .base = 0x5d000000, + .irq = 28 + OMAP_INTC_START, + .pdata = { + .name = "iva2", + .nr_tlb_entries = 32, + .clk_name = "iva2_ck", + .da_start = 0x11000000, + .da_end = 0xFFFFF000, + }, + }, +#endif +}; +#define NR_OMAP3_IOMMU_DEVICES ARRAY_SIZE(omap3_devices) +static struct platform_device *omap3_iommu_pdev[NR_OMAP3_IOMMU_DEVICES]; +#else +#define omap3_devices NULL +#define NR_OMAP3_IOMMU_DEVICES 0 +#define omap3_iommu_pdev NULL +#endif + +#ifdef CONFIG_ARCH_OMAP4 +static struct iommu_device omap4_devices[] = { + { + .base = OMAP4_MMU1_BASE, + .irq = 100 + OMAP44XX_IRQ_GIC_START, + .pdata = { + .name = "ducati", + .nr_tlb_entries = 32, + .clk_name = "ipu_fck", + .da_start = 0x0, + .da_end = 0xFFFFF000, + }, + }, + { + .base = OMAP4_MMU2_BASE, + .irq = 28 + OMAP44XX_IRQ_GIC_START, + .pdata = { + .name = "tesla", + .nr_tlb_entries = 32, + .clk_name = "dsp_fck", + .da_start = 0x0, + .da_end = 0xFFFFF000, + }, + }, +}; +#define NR_OMAP4_IOMMU_DEVICES ARRAY_SIZE(omap4_devices) +static struct platform_device *omap4_iommu_pdev[NR_OMAP4_IOMMU_DEVICES]; +#else +#define omap4_devices NULL +#define NR_OMAP4_IOMMU_DEVICES 0 +#define omap4_iommu_pdev NULL +#endif + +static struct platform_device **omap_iommu_pdev; + +static int __init omap_iommu_init(void) { - struct platform_device *pdev; - struct iommu_platform_data *pdata; - struct omap_mmu_dev_attr *a = (struct omap_mmu_dev_attr *)oh->dev_attr; - static int i; - - pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); - if (!pdata) - return -ENOMEM; - - pdata->name = oh->name; - pdata->nr_tlb_entries = a->nr_tlb_entries; - pdata->da_start = a->da_start; - pdata->da_end = a->da_end; - - if (oh->rst_lines_cnt == 1) { - pdata->reset_name = oh->rst_lines->name; - pdata->assert_reset = omap_device_assert_hardreset; - pdata->deassert_reset = omap_device_deassert_hardreset; - } + int i, err; + struct resource res[] = { + { .flags = IORESOURCE_MEM }, + { .flags = IORESOURCE_IRQ }, + }; - pdev = omap_device_build("omap-iommu", i, oh, pdata, sizeof(*pdata), - NULL, 0, 0); + if (cpu_is_omap34xx()) { + devices = omap3_devices; + omap_iommu_pdev = omap3_iommu_pdev; + num_iommu_devices = NR_OMAP3_IOMMU_DEVICES; + } else if (cpu_is_omap44xx()) { + devices = omap4_devices; + omap_iommu_pdev = omap4_iommu_pdev; + num_iommu_devices = NR_OMAP4_IOMMU_DEVICES; + } else + return -ENODEV; - kfree(pdata); + for (i = 0; i < num_iommu_devices; i++) { + struct platform_device *pdev; + const struct iommu_device *d = &devices[i]; - if (IS_ERR(pdev)) { - pr_err("%s: device build err: %ld\n", __func__, PTR_ERR(pdev)); - return PTR_ERR(pdev); - } + pdev = platform_device_alloc("omap-iommu", i); + if (!pdev) { + err = -ENOMEM; + goto err_out; + } - i++; + res[0].start = d->base; + res[0].end = d->base + MMU_REG_SIZE - 1; + res[1].start = res[1].end = d->irq; + err = platform_device_add_resources(pdev, res, + ARRAY_SIZE(res)); + if (err) + goto err_out; + err = platform_device_add_data(pdev, &d->pdata, + sizeof(d->pdata)); + if (err) + goto err_out; + err = platform_device_add(pdev); + if (err) + goto err_out; + omap_iommu_pdev[i] = pdev; + } return 0; -} -static int __init omap_iommu_init(void) -{ - return omap_hwmod_for_each_by_class("mmu", omap_iommu_dev_init, NULL); +err_out: + while (i--) + platform_device_put(omap_iommu_pdev[i]); + return err; } /* must be ready before omap3isp is probed */ subsys_initcall(omap_iommu_init); static void __exit omap_iommu_exit(void) { - /* Do nothing */ + int i; + + for (i = 0; i < num_iommu_devices; i++) + platform_device_unregister(omap_iommu_pdev[i]); } module_exit(omap_iommu_exit); diff --git a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c index f9fab942d5ba..272b0178dba6 100644 --- a/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c +++ b/trunk/arch/arm/mach-omap2/omap_hwmod_44xx_data.c @@ -653,7 +653,7 @@ static struct omap_hwmod omap44xx_dsp_hwmod = { .mpu_irqs = omap44xx_dsp_irqs, .rst_lines = omap44xx_dsp_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_dsp_resets), - .main_clk = "dpll_iva_m4x2_ck", + .main_clk = "dsp_fck", .prcm = { .omap4 = { .clkctrl_offs = OMAP4_CM_TESLA_TESLA_CLKCTRL_OFFSET, @@ -1679,7 +1679,7 @@ static struct omap_hwmod omap44xx_ipu_hwmod = { .mpu_irqs = omap44xx_ipu_irqs, .rst_lines = omap44xx_ipu_resets, .rst_lines_cnt = ARRAY_SIZE(omap44xx_ipu_resets), - .main_clk = "ducati_clk_mux_ck", + .main_clk = "ipu_fck", .prcm = { .omap4 = { .clkctrl_offs = OMAP4_CM_DUCATI_DUCATI_CLKCTRL_OFFSET, diff --git a/trunk/arch/arm64/include/asm/dma-mapping.h b/trunk/arch/arm64/include/asm/dma-mapping.h index 994776894198..538f4b44db5d 100644 --- a/trunk/arch/arm64/include/asm/dma-mapping.h +++ b/trunk/arch/arm64/include/asm/dma-mapping.h @@ -50,7 +50,6 @@ static inline phys_addr_t dma_to_phys(struct device *dev, dma_addr_t dev_addr) static inline int dma_mapping_error(struct device *dev, dma_addr_t dev_addr) { struct dma_map_ops *ops = get_dma_ops(dev); - debug_dma_mapping_error(dev, dev_addr); return ops->mapping_error(dev, dev_addr); } diff --git a/trunk/arch/c6x/include/asm/dma-mapping.h b/trunk/arch/c6x/include/asm/dma-mapping.h index 3c694065030f..03579fd99dba 100644 --- a/trunk/arch/c6x/include/asm/dma-mapping.h +++ b/trunk/arch/c6x/include/asm/dma-mapping.h @@ -32,7 +32,6 @@ static inline int dma_set_mask(struct device *dev, u64 dma_mask) */ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - debug_dma_mapping_error(dev, dma_addr); return dma_addr == ~0; } diff --git a/trunk/arch/ia64/include/asm/dma-mapping.h b/trunk/arch/ia64/include/asm/dma-mapping.h index cf3ab7e784b5..4f5e8148440d 100644 --- a/trunk/arch/ia64/include/asm/dma-mapping.h +++ b/trunk/arch/ia64/include/asm/dma-mapping.h @@ -58,7 +58,6 @@ static inline void dma_free_attrs(struct device *dev, size_t size, static inline int dma_mapping_error(struct device *dev, dma_addr_t daddr) { struct dma_map_ops *ops = platform_dma_get_ops(dev); - debug_dma_mapping_error(dev, daddr); return ops->mapping_error(dev, daddr); } diff --git a/trunk/arch/microblaze/include/asm/dma-mapping.h b/trunk/arch/microblaze/include/asm/dma-mapping.h index 46460f1c49c4..01d228286cb0 100644 --- a/trunk/arch/microblaze/include/asm/dma-mapping.h +++ b/trunk/arch/microblaze/include/asm/dma-mapping.h @@ -114,8 +114,6 @@ static inline void __dma_sync(unsigned long paddr, static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { struct dma_map_ops *ops = get_dma_ops(dev); - - debug_dma_mapping_error(dev, dma_addr); if (ops->mapping_error) return ops->mapping_error(dev, dma_addr); diff --git a/trunk/arch/mips/include/asm/dma-mapping.h b/trunk/arch/mips/include/asm/dma-mapping.h index 006b43e38a9c..be39a12901c6 100644 --- a/trunk/arch/mips/include/asm/dma-mapping.h +++ b/trunk/arch/mips/include/asm/dma-mapping.h @@ -40,8 +40,6 @@ static inline int dma_supported(struct device *dev, u64 mask) static inline int dma_mapping_error(struct device *dev, u64 mask) { struct dma_map_ops *ops = get_dma_ops(dev); - - debug_dma_mapping_error(dev, mask); return ops->mapping_error(dev, mask); } diff --git a/trunk/arch/powerpc/include/asm/dma-mapping.h b/trunk/arch/powerpc/include/asm/dma-mapping.h index e27e9ad6818e..78160874809a 100644 --- a/trunk/arch/powerpc/include/asm/dma-mapping.h +++ b/trunk/arch/powerpc/include/asm/dma-mapping.h @@ -172,7 +172,6 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { struct dma_map_ops *dma_ops = get_dma_ops(dev); - debug_dma_mapping_error(dev, dma_addr); if (dma_ops->mapping_error) return dma_ops->mapping_error(dev, dma_addr); diff --git a/trunk/arch/sh/include/asm/dma-mapping.h b/trunk/arch/sh/include/asm/dma-mapping.h index b437f2c780b8..8bd965e00a15 100644 --- a/trunk/arch/sh/include/asm/dma-mapping.h +++ b/trunk/arch/sh/include/asm/dma-mapping.h @@ -46,7 +46,6 @@ static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { struct dma_map_ops *ops = get_dma_ops(dev); - debug_dma_mapping_error(dev, dma_addr); if (ops->mapping_error) return ops->mapping_error(dev, dma_addr); diff --git a/trunk/arch/sparc/include/asm/dma-mapping.h b/trunk/arch/sparc/include/asm/dma-mapping.h index 05fe53f5346e..8493fd3c7ba5 100644 --- a/trunk/arch/sparc/include/asm/dma-mapping.h +++ b/trunk/arch/sparc/include/asm/dma-mapping.h @@ -59,7 +59,6 @@ static inline void dma_free_attrs(struct device *dev, size_t size, static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - debug_dma_mapping_error(dev, dma_addr); return (dma_addr == DMA_ERROR_CODE); } diff --git a/trunk/arch/tile/include/asm/dma-mapping.h b/trunk/arch/tile/include/asm/dma-mapping.h index f2ff191376b4..4b6247d1a315 100644 --- a/trunk/arch/tile/include/asm/dma-mapping.h +++ b/trunk/arch/tile/include/asm/dma-mapping.h @@ -72,7 +72,6 @@ static inline bool dma_capable(struct device *dev, dma_addr_t addr, size_t size) static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { - debug_dma_mapping_error(dev, dma_addr); return get_dma_ops(dev)->mapping_error(dev, dma_addr); } diff --git a/trunk/arch/x86/include/asm/dma-mapping.h b/trunk/arch/x86/include/asm/dma-mapping.h index 808dae63eeea..f7b4c7903e7e 100644 --- a/trunk/arch/x86/include/asm/dma-mapping.h +++ b/trunk/arch/x86/include/asm/dma-mapping.h @@ -47,7 +47,6 @@ static inline struct dma_map_ops *get_dma_ops(struct device *dev) static inline int dma_mapping_error(struct device *dev, dma_addr_t dma_addr) { struct dma_map_ops *ops = get_dma_ops(dev); - debug_dma_mapping_error(dev, dma_addr); if (ops->mapping_error) return ops->mapping_error(dev, dma_addr); diff --git a/trunk/drivers/block/rbd.c b/trunk/drivers/block/rbd.c index 89576a0b3f2e..bb3d9be3b1b4 100644 --- a/trunk/drivers/block/rbd.c +++ b/trunk/drivers/block/rbd.c @@ -61,29 +61,15 @@ #define RBD_MINORS_PER_MAJOR 256 /* max minors per blkdev */ -#define RBD_SNAP_DEV_NAME_PREFIX "snap_" -#define RBD_MAX_SNAP_NAME_LEN \ - (NAME_MAX - (sizeof (RBD_SNAP_DEV_NAME_PREFIX) - 1)) - +#define RBD_MAX_SNAP_NAME_LEN 32 #define RBD_MAX_SNAP_COUNT 510 /* allows max snapc to fit in 4KB */ #define RBD_MAX_OPT_LEN 1024 #define RBD_SNAP_HEAD_NAME "-" -/* This allows a single page to hold an image name sent by OSD */ -#define RBD_IMAGE_NAME_LEN_MAX (PAGE_SIZE - sizeof (__le32) - 1) #define RBD_IMAGE_ID_LEN_MAX 64 - #define RBD_OBJ_PREFIX_LEN_MAX 64 -/* Feature bits */ - -#define RBD_FEATURE_LAYERING 1 - -/* Features supported by this (client software) implementation. */ - -#define RBD_FEATURES_ALL (0) - /* * An RBD device name will be "rbd#", where the "rbd" comes from * RBD_DRV_NAME above, and # is a unique integer identifier. @@ -115,27 +101,6 @@ struct rbd_image_header { u64 obj_version; }; -/* - * An rbd image specification. - * - * The tuple (pool_id, image_id, snap_id) is sufficient to uniquely - * identify an image. - */ -struct rbd_spec { - u64 pool_id; - char *pool_name; - - char *image_id; - size_t image_id_len; - char *image_name; - size_t image_name_len; - - u64 snap_id; - char *snap_name; - - struct kref kref; -}; - struct rbd_options { bool read_only; }; @@ -190,8 +155,11 @@ struct rbd_snap { }; struct rbd_mapping { + char *snap_name; + u64 snap_id; u64 size; u64 features; + bool snap_exists; bool read_only; }; @@ -205,6 +173,7 @@ struct rbd_device { struct gendisk *disk; /* blkdev's gendisk and rq */ u32 image_format; /* Either 1 or 2 */ + struct rbd_options rbd_opts; struct rbd_client *rbd_client; char name[DEV_NAME_LEN]; /* blkdev name, e.g. rbd3 */ @@ -212,17 +181,17 @@ struct rbd_device { spinlock_t lock; /* queue lock */ struct rbd_image_header header; - bool exists; - struct rbd_spec *spec; - + char *image_id; + size_t image_id_len; + char *image_name; + size_t image_name_len; char *header_name; + char *pool_name; + int pool_id; struct ceph_osd_event *watch_event; struct ceph_osd_request *watch_request; - struct rbd_spec *parent_spec; - u64 parent_overlap; - /* protects updating the header */ struct rw_semaphore header_rwsem; @@ -235,7 +204,6 @@ struct rbd_device { /* sysfs related */ struct device dev; - unsigned long open_count; }; static DEFINE_MUTEX(ctl_mutex); /* Serialize open/close/setup/teardown */ @@ -250,7 +218,7 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev); static int rbd_dev_snaps_register(struct rbd_device *rbd_dev); static void rbd_dev_release(struct device *dev); -static void rbd_remove_snap_dev(struct rbd_snap *snap); +static void __rbd_remove_snap_dev(struct rbd_snap *snap); static ssize_t rbd_add(struct bus_type *bus, const char *buf, size_t count); @@ -290,8 +258,17 @@ static struct device rbd_root_dev = { # define rbd_assert(expr) ((void) 0) #endif /* !RBD_DEBUG */ -static int rbd_dev_refresh(struct rbd_device *rbd_dev, u64 *hver); -static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver); +static struct device *rbd_get_dev(struct rbd_device *rbd_dev) +{ + return get_device(&rbd_dev->dev); +} + +static void rbd_put_dev(struct rbd_device *rbd_dev) +{ + put_device(&rbd_dev->dev); +} + +static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver); static int rbd_open(struct block_device *bdev, fmode_t mode) { @@ -300,11 +277,8 @@ static int rbd_open(struct block_device *bdev, fmode_t mode) if ((mode & FMODE_WRITE) && rbd_dev->mapping.read_only) return -EROFS; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - (void) get_device(&rbd_dev->dev); + rbd_get_dev(rbd_dev); set_device_ro(bdev, rbd_dev->mapping.read_only); - rbd_dev->open_count++; - mutex_unlock(&ctl_mutex); return 0; } @@ -313,11 +287,7 @@ static int rbd_release(struct gendisk *disk, fmode_t mode) { struct rbd_device *rbd_dev = disk->private_data; - mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - rbd_assert(rbd_dev->open_count > 0); - rbd_dev->open_count--; - put_device(&rbd_dev->dev); - mutex_unlock(&ctl_mutex); + rbd_put_dev(rbd_dev); return 0; } @@ -418,7 +388,7 @@ enum { static match_table_t rbd_opts_tokens = { /* int args above */ /* string args above */ - {Opt_read_only, "read_only"}, + {Opt_read_only, "mapping.read_only"}, {Opt_read_only, "ro"}, /* Alternate spelling */ {Opt_read_write, "read_write"}, {Opt_read_write, "rw"}, /* Alternate spelling */ @@ -471,17 +441,33 @@ static int parse_rbd_opts_token(char *c, void *private) * Get a ceph client with specific addr and configuration, if one does * not exist create it. */ -static struct rbd_client *rbd_get_client(struct ceph_options *ceph_opts) +static int rbd_get_client(struct rbd_device *rbd_dev, const char *mon_addr, + size_t mon_addr_len, char *options) { + struct rbd_options *rbd_opts = &rbd_dev->rbd_opts; + struct ceph_options *ceph_opts; struct rbd_client *rbdc; + rbd_opts->read_only = RBD_READ_ONLY_DEFAULT; + + ceph_opts = ceph_parse_options(options, mon_addr, + mon_addr + mon_addr_len, + parse_rbd_opts_token, rbd_opts); + if (IS_ERR(ceph_opts)) + return PTR_ERR(ceph_opts); + rbdc = rbd_client_find(ceph_opts); - if (rbdc) /* using an existing client */ + if (rbdc) { + /* using an existing client */ ceph_destroy_options(ceph_opts); - else + } else { rbdc = rbd_client_create(ceph_opts); + if (IS_ERR(rbdc)) + return PTR_ERR(rbdc); + } + rbd_dev->rbd_client = rbdc; - return rbdc; + return 0; } /* @@ -506,10 +492,10 @@ static void rbd_client_release(struct kref *kref) * Drop reference to ceph client node. If it's not referenced anymore, release * it. */ -static void rbd_put_client(struct rbd_client *rbdc) +static void rbd_put_client(struct rbd_device *rbd_dev) { - if (rbdc) - kref_put(&rbdc->kref, rbd_client_release); + kref_put(&rbd_dev->rbd_client->kref, rbd_client_release); + rbd_dev->rbd_client = NULL; } /* @@ -538,16 +524,6 @@ static bool rbd_dev_ondisk_valid(struct rbd_image_header_ondisk *ondisk) if (memcmp(&ondisk->text, RBD_HEADER_TEXT, sizeof (RBD_HEADER_TEXT))) return false; - /* The bio layer requires at least sector-sized I/O */ - - if (ondisk->options.order < SECTOR_SHIFT) - return false; - - /* If we use u64 in a few spots we may be able to loosen this */ - - if (ondisk->options.order > 8 * sizeof (int) - 1) - return false; - /* * The size of a snapshot header has to fit in a size_t, and * that limits the number of snapshots. @@ -659,20 +635,6 @@ static int rbd_header_from_disk(struct rbd_image_header *header, return -ENOMEM; } -static const char *rbd_snap_name(struct rbd_device *rbd_dev, u64 snap_id) -{ - struct rbd_snap *snap; - - if (snap_id == CEPH_NOSNAP) - return RBD_SNAP_HEAD_NAME; - - list_for_each_entry(snap, &rbd_dev->snaps, node) - if (snap_id == snap->id) - return snap->name; - - return NULL; -} - static int snap_by_name(struct rbd_device *rbd_dev, const char *snap_name) { @@ -680,7 +642,7 @@ static int snap_by_name(struct rbd_device *rbd_dev, const char *snap_name) list_for_each_entry(snap, &rbd_dev->snaps, node) { if (!strcmp(snap_name, snap->name)) { - rbd_dev->spec->snap_id = snap->id; + rbd_dev->mapping.snap_id = snap->id; rbd_dev->mapping.size = snap->size; rbd_dev->mapping.features = snap->features; @@ -691,23 +653,26 @@ static int snap_by_name(struct rbd_device *rbd_dev, const char *snap_name) return -ENOENT; } -static int rbd_dev_set_mapping(struct rbd_device *rbd_dev) +static int rbd_dev_set_mapping(struct rbd_device *rbd_dev, char *snap_name) { int ret; - if (!memcmp(rbd_dev->spec->snap_name, RBD_SNAP_HEAD_NAME, + if (!memcmp(snap_name, RBD_SNAP_HEAD_NAME, sizeof (RBD_SNAP_HEAD_NAME))) { - rbd_dev->spec->snap_id = CEPH_NOSNAP; + rbd_dev->mapping.snap_id = CEPH_NOSNAP; rbd_dev->mapping.size = rbd_dev->header.image_size; rbd_dev->mapping.features = rbd_dev->header.features; + rbd_dev->mapping.snap_exists = false; + rbd_dev->mapping.read_only = rbd_dev->rbd_opts.read_only; ret = 0; } else { - ret = snap_by_name(rbd_dev, rbd_dev->spec->snap_name); + ret = snap_by_name(rbd_dev, snap_name); if (ret < 0) goto done; + rbd_dev->mapping.snap_exists = true; rbd_dev->mapping.read_only = true; } - rbd_dev->exists = true; + rbd_dev->mapping.snap_name = snap_name; done: return ret; } @@ -730,13 +695,13 @@ static char *rbd_segment_name(struct rbd_device *rbd_dev, u64 offset) u64 segment; int ret; - name = kmalloc(MAX_OBJ_NAME_SIZE + 1, GFP_NOIO); + name = kmalloc(RBD_MAX_SEG_NAME_LEN + 1, GFP_NOIO); if (!name) return NULL; segment = offset >> rbd_dev->header.obj_order; - ret = snprintf(name, MAX_OBJ_NAME_SIZE + 1, "%s.%012llx", + ret = snprintf(name, RBD_MAX_SEG_NAME_LEN, "%s.%012llx", rbd_dev->header.object_prefix, segment); - if (ret < 0 || ret > MAX_OBJ_NAME_SIZE) { + if (ret < 0 || ret >= RBD_MAX_SEG_NAME_LEN) { pr_err("error formatting segment name for #%llu (%d)\n", segment, ret); kfree(name); @@ -835,144 +800,77 @@ static void zero_bio_chain(struct bio *chain, int start_ofs) } /* - * Clone a portion of a bio, starting at the given byte offset - * and continuing for the number of bytes indicated. + * bio_chain_clone - clone a chain of bios up to a certain length. + * might return a bio_pair that will need to be released. */ -static struct bio *bio_clone_range(struct bio *bio_src, - unsigned int offset, - unsigned int len, - gfp_t gfpmask) -{ - struct bio_vec *bv; - unsigned int resid; - unsigned short idx; - unsigned int voff; - unsigned short end_idx; - unsigned short vcnt; - struct bio *bio; - - /* Handle the easy case for the caller */ - - if (!offset && len == bio_src->bi_size) - return bio_clone(bio_src, gfpmask); - - if (WARN_ON_ONCE(!len)) - return NULL; - if (WARN_ON_ONCE(len > bio_src->bi_size)) - return NULL; - if (WARN_ON_ONCE(offset > bio_src->bi_size - len)) - return NULL; - - /* Find first affected segment... */ - - resid = offset; - __bio_for_each_segment(bv, bio_src, idx, 0) { - if (resid < bv->bv_len) - break; - resid -= bv->bv_len; +static struct bio *bio_chain_clone(struct bio **old, struct bio **next, + struct bio_pair **bp, + int len, gfp_t gfpmask) +{ + struct bio *old_chain = *old; + struct bio *new_chain = NULL; + struct bio *tail; + int total = 0; + + if (*bp) { + bio_pair_release(*bp); + *bp = NULL; } - voff = resid; - /* ...and the last affected segment */ + while (old_chain && (total < len)) { + struct bio *tmp; - resid += len; - __bio_for_each_segment(bv, bio_src, end_idx, idx) { - if (resid <= bv->bv_len) - break; - resid -= bv->bv_len; - } - vcnt = end_idx - idx + 1; - - /* Build the clone */ - - bio = bio_alloc(gfpmask, (unsigned int) vcnt); - if (!bio) - return NULL; /* ENOMEM */ - - bio->bi_bdev = bio_src->bi_bdev; - bio->bi_sector = bio_src->bi_sector + (offset >> SECTOR_SHIFT); - bio->bi_rw = bio_src->bi_rw; - bio->bi_flags |= 1 << BIO_CLONED; - - /* - * Copy over our part of the bio_vec, then update the first - * and last (or only) entries. - */ - memcpy(&bio->bi_io_vec[0], &bio_src->bi_io_vec[idx], - vcnt * sizeof (struct bio_vec)); - bio->bi_io_vec[0].bv_offset += voff; - if (vcnt > 1) { - bio->bi_io_vec[0].bv_len -= voff; - bio->bi_io_vec[vcnt - 1].bv_len = resid; - } else { - bio->bi_io_vec[0].bv_len = len; - } + tmp = bio_kmalloc(gfpmask, old_chain->bi_max_vecs); + if (!tmp) + goto err_out; + gfpmask &= ~__GFP_WAIT; /* can't wait after the first */ - bio->bi_vcnt = vcnt; - bio->bi_size = len; - bio->bi_idx = 0; + if (total + old_chain->bi_size > len) { + struct bio_pair *bp; - return bio; -} + /* + * this split can only happen with a single paged bio, + * split_bio will BUG_ON if this is not the case + */ + dout("bio_chain_clone split! total=%d remaining=%d" + "bi_size=%u\n", + total, len - total, old_chain->bi_size); -/* - * Clone a portion of a bio chain, starting at the given byte offset - * into the first bio in the source chain and continuing for the - * number of bytes indicated. The result is another bio chain of - * exactly the given length, or a null pointer on error. - * - * The bio_src and offset parameters are both in-out. On entry they - * refer to the first source bio and the offset into that bio where - * the start of data to be cloned is located. - * - * On return, bio_src is updated to refer to the bio in the source - * chain that contains first un-cloned byte, and *offset will - * contain the offset of that byte within that bio. - */ -static struct bio *bio_chain_clone_range(struct bio **bio_src, - unsigned int *offset, - unsigned int len, - gfp_t gfpmask) -{ - struct bio *bi = *bio_src; - unsigned int off = *offset; - struct bio *chain = NULL; - struct bio **end; + /* split the bio. We'll release it either in the next + call, or it will have to be released outside */ + bp = bio_split(old_chain, (len - total) / SECTOR_SIZE); + if (!bp) + goto err_out; - /* Build up a chain of clone bios up to the limit */ + __bio_clone(tmp, &bp->bio1); - if (!bi || off >= bi->bi_size || !len) - return NULL; /* Nothing to clone */ + *next = &bp->bio2; + } else { + __bio_clone(tmp, old_chain); + *next = old_chain->bi_next; + } - end = &chain; - while (len) { - unsigned int bi_size; - struct bio *bio; + tmp->bi_bdev = NULL; + tmp->bi_next = NULL; + if (new_chain) + tail->bi_next = tmp; + else + new_chain = tmp; + tail = tmp; + old_chain = old_chain->bi_next; - if (!bi) - goto out_err; /* EINVAL; ran out of bio's */ - bi_size = min_t(unsigned int, bi->bi_size - off, len); - bio = bio_clone_range(bi, off, bi_size, gfpmask); - if (!bio) - goto out_err; /* ENOMEM */ + total += tmp->bi_size; + } - *end = bio; - end = &bio->bi_next; + rbd_assert(total == len); - off += bi_size; - if (off == bi->bi_size) { - bi = bi->bi_next; - off = 0; - } - len -= bi_size; - } - *bio_src = bi; - *offset = off; + *old = old_chain; - return chain; -out_err: - bio_chain_put(chain); + return new_chain; +err_out: + dout("bio_chain_clone with err\n"); + bio_chain_put(new_chain); return NULL; } @@ -1090,9 +988,8 @@ static int rbd_do_request(struct request *rq, req_data->coll_index = coll_index; } - dout("rbd_do_request object_name=%s ofs=%llu len=%llu coll=%p[%d]\n", - object_name, (unsigned long long) ofs, - (unsigned long long) len, coll, coll_index); + dout("rbd_do_request object_name=%s ofs=%llu len=%llu\n", object_name, + (unsigned long long) ofs, (unsigned long long) len); osdc = &rbd_dev->rbd_client->client->osdc; req = ceph_osdc_alloc_request(osdc, flags, snapc, ops, @@ -1122,7 +1019,7 @@ static int rbd_do_request(struct request *rq, layout->fl_stripe_unit = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); layout->fl_stripe_count = cpu_to_le32(1); layout->fl_object_size = cpu_to_le32(1 << RBD_MAX_OBJ_ORDER); - layout->fl_pg_pool = cpu_to_le32((int) rbd_dev->spec->pool_id); + layout->fl_pg_pool = cpu_to_le32(rbd_dev->pool_id); ret = ceph_calc_raw_layout(osdc, layout, snapid, ofs, &len, &bno, req, ops); rbd_assert(ret == 0); @@ -1257,6 +1154,8 @@ static int rbd_req_sync_op(struct rbd_device *rbd_dev, static int rbd_do_op(struct request *rq, struct rbd_device *rbd_dev, struct ceph_snap_context *snapc, + u64 snapid, + int opcode, int flags, u64 ofs, u64 len, struct bio *bio, struct rbd_req_coll *coll, @@ -1268,9 +1167,6 @@ static int rbd_do_op(struct request *rq, int ret; struct ceph_osd_req_op *ops; u32 payload_len; - int opcode; - int flags; - u64 snapid; seg_name = rbd_segment_name(rbd_dev, ofs); if (!seg_name) @@ -1278,18 +1174,7 @@ static int rbd_do_op(struct request *rq, seg_len = rbd_segment_length(rbd_dev, ofs, len); seg_ofs = rbd_segment_offset(rbd_dev, ofs); - if (rq_data_dir(rq) == WRITE) { - opcode = CEPH_OSD_OP_WRITE; - flags = CEPH_OSD_FLAG_WRITE|CEPH_OSD_FLAG_ONDISK; - snapid = CEPH_NOSNAP; - payload_len = seg_len; - } else { - opcode = CEPH_OSD_OP_READ; - flags = CEPH_OSD_FLAG_READ; - snapc = NULL; - snapid = rbd_dev->spec->snap_id; - payload_len = 0; - } + payload_len = (flags & CEPH_OSD_FLAG_WRITE ? seg_len : 0); ret = -ENOMEM; ops = rbd_create_rw_ops(1, opcode, payload_len); @@ -1316,6 +1201,41 @@ static int rbd_do_op(struct request *rq, return ret; } +/* + * Request async osd write + */ +static int rbd_req_write(struct request *rq, + struct rbd_device *rbd_dev, + struct ceph_snap_context *snapc, + u64 ofs, u64 len, + struct bio *bio, + struct rbd_req_coll *coll, + int coll_index) +{ + return rbd_do_op(rq, rbd_dev, snapc, CEPH_NOSNAP, + CEPH_OSD_OP_WRITE, + CEPH_OSD_FLAG_WRITE | CEPH_OSD_FLAG_ONDISK, + ofs, len, bio, coll, coll_index); +} + +/* + * Request async osd read + */ +static int rbd_req_read(struct request *rq, + struct rbd_device *rbd_dev, + u64 snapid, + u64 ofs, u64 len, + struct bio *bio, + struct rbd_req_coll *coll, + int coll_index) +{ + return rbd_do_op(rq, rbd_dev, NULL, + snapid, + CEPH_OSD_OP_READ, + CEPH_OSD_FLAG_READ, + ofs, len, bio, coll, coll_index); +} + /* * Request sync osd read */ @@ -1384,7 +1304,7 @@ static void rbd_watch_cb(u64 ver, u64 notify_id, u8 opcode, void *data) dout("rbd_watch_cb %s notify_id=%llu opcode=%u\n", rbd_dev->header_name, (unsigned long long) notify_id, (unsigned int) opcode); - rc = rbd_dev_refresh(rbd_dev, &hver); + rc = rbd_refresh_header(rbd_dev, &hver); if (rc) pr_warning(RBD_DRV_NAME "%d got notification but failed to " " update snaps: %d\n", rbd_dev->major, rc); @@ -1540,16 +1460,18 @@ static void rbd_rq_fn(struct request_queue *q) { struct rbd_device *rbd_dev = q->queuedata; struct request *rq; + struct bio_pair *bp = NULL; while ((rq = blk_fetch_request(q))) { struct bio *bio; + struct bio *rq_bio, *next_bio = NULL; bool do_write; unsigned int size; + u64 op_size = 0; u64 ofs; int num_segs, cur_seg = 0; struct rbd_req_coll *coll; struct ceph_snap_context *snapc; - unsigned int bio_offset; dout("fetched request\n"); @@ -1561,6 +1483,10 @@ static void rbd_rq_fn(struct request_queue *q) /* deduce our operation (read, write) */ do_write = (rq_data_dir(rq) == WRITE); + + size = blk_rq_bytes(rq); + ofs = blk_rq_pos(rq) * SECTOR_SIZE; + rq_bio = rq->bio; if (do_write && rbd_dev->mapping.read_only) { __blk_end_request_all(rq, -EROFS); continue; @@ -1570,8 +1496,8 @@ static void rbd_rq_fn(struct request_queue *q) down_read(&rbd_dev->header_rwsem); - if (!rbd_dev->exists) { - rbd_assert(rbd_dev->spec->snap_id != CEPH_NOSNAP); + if (rbd_dev->mapping.snap_id != CEPH_NOSNAP && + !rbd_dev->mapping.snap_exists) { up_read(&rbd_dev->header_rwsem); dout("request for non-existent snapshot"); spin_lock_irq(q->queue_lock); @@ -1583,10 +1509,6 @@ static void rbd_rq_fn(struct request_queue *q) up_read(&rbd_dev->header_rwsem); - size = blk_rq_bytes(rq); - ofs = blk_rq_pos(rq) * SECTOR_SIZE; - bio = rq->bio; - dout("%s 0x%x bytes at 0x%llx\n", do_write ? "write" : "read", size, (unsigned long long) blk_rq_pos(rq) * SECTOR_SIZE); @@ -1606,37 +1528,45 @@ static void rbd_rq_fn(struct request_queue *q) continue; } - bio_offset = 0; do { - u64 limit = rbd_segment_length(rbd_dev, ofs, size); - unsigned int chain_size; - struct bio *bio_chain; - - BUG_ON(limit > (u64) UINT_MAX); - chain_size = (unsigned int) limit; + /* a bio clone to be passed down to OSD req */ dout("rq->bio->bi_vcnt=%hu\n", rq->bio->bi_vcnt); - + op_size = rbd_segment_length(rbd_dev, ofs, size); kref_get(&coll->kref); + bio = bio_chain_clone(&rq_bio, &next_bio, &bp, + op_size, GFP_ATOMIC); + if (!bio) { + rbd_coll_end_req_index(rq, coll, cur_seg, + -ENOMEM, op_size); + goto next_seg; + } - /* Pass a cloned bio chain via an osd request */ - bio_chain = bio_chain_clone_range(&bio, - &bio_offset, chain_size, - GFP_ATOMIC); - if (bio_chain) - (void) rbd_do_op(rq, rbd_dev, snapc, - ofs, chain_size, - bio_chain, coll, cur_seg); + /* init OSD command: write or read */ + if (do_write) + rbd_req_write(rq, rbd_dev, + snapc, + ofs, + op_size, bio, + coll, cur_seg); else - rbd_coll_end_req_index(rq, coll, cur_seg, - -ENOMEM, chain_size); - size -= chain_size; - ofs += chain_size; + rbd_req_read(rq, rbd_dev, + rbd_dev->mapping.snap_id, + ofs, + op_size, bio, + coll, cur_seg); + +next_seg: + size -= op_size; + ofs += op_size; cur_seg++; + rq_bio = next_bio; } while (size > 0); kref_put(&coll->kref, rbd_coll_release); + if (bp) + bio_pair_release(bp); spin_lock_irq(q->queue_lock); ceph_put_snap_context(snapc); @@ -1646,47 +1576,28 @@ static void rbd_rq_fn(struct request_queue *q) /* * a queue callback. Makes sure that we don't create a bio that spans across * multiple osd objects. One exception would be with a single page bios, - * which we handle later at bio_chain_clone_range() + * which we handle later at bio_chain_clone */ static int rbd_merge_bvec(struct request_queue *q, struct bvec_merge_data *bmd, struct bio_vec *bvec) { struct rbd_device *rbd_dev = q->queuedata; - sector_t sector_offset; - sector_t sectors_per_obj; - sector_t obj_sector_offset; - int ret; + unsigned int chunk_sectors; + sector_t sector; + unsigned int bio_sectors; + int max; - /* - * Find how far into its rbd object the partition-relative - * bio start sector is to offset relative to the enclosing - * device. - */ - sector_offset = get_start_sect(bmd->bi_bdev) + bmd->bi_sector; - sectors_per_obj = 1 << (rbd_dev->header.obj_order - SECTOR_SHIFT); - obj_sector_offset = sector_offset & (sectors_per_obj - 1); - - /* - * Compute the number of bytes from that offset to the end - * of the object. Account for what's already used by the bio. - */ - ret = (int) (sectors_per_obj - obj_sector_offset) << SECTOR_SHIFT; - if (ret > bmd->bi_size) - ret -= bmd->bi_size; - else - ret = 0; + chunk_sectors = 1 << (rbd_dev->header.obj_order - SECTOR_SHIFT); + sector = bmd->bi_sector + get_start_sect(bmd->bi_bdev); + bio_sectors = bmd->bi_size >> SECTOR_SHIFT; - /* - * Don't send back more than was asked for. And if the bio - * was empty, let the whole thing through because: "Note - * that a block device *must* allow a single page to be - * added to an empty bio." - */ - rbd_assert(bvec->bv_len <= PAGE_SIZE); - if (ret > (int) bvec->bv_len || !bmd->bi_size) - ret = (int) bvec->bv_len; - - return ret; + max = (chunk_sectors - ((sector & (chunk_sectors - 1)) + + bio_sectors)) << SECTOR_SHIFT; + if (max < 0) + max = 0; /* bio_add cannot handle a negative return */ + if (max <= bvec->bv_len && bio_sectors == 0) + return bvec->bv_len; + return max; } static void rbd_free_disk(struct rbd_device *rbd_dev) @@ -1752,13 +1663,13 @@ rbd_dev_v1_header_read(struct rbd_device *rbd_dev, u64 *version) ret = -ENXIO; pr_warning("short header read for image %s" " (want %zd got %d)\n", - rbd_dev->spec->image_name, size, ret); + rbd_dev->image_name, size, ret); goto out_err; } if (!rbd_dev_ondisk_valid(ondisk)) { ret = -ENXIO; pr_warning("invalid header for image %s\n", - rbd_dev->spec->image_name); + rbd_dev->image_name); goto out_err; } @@ -1796,32 +1707,19 @@ static int rbd_read_header(struct rbd_device *rbd_dev, return ret; } -static void rbd_remove_all_snaps(struct rbd_device *rbd_dev) +static void __rbd_remove_all_snaps(struct rbd_device *rbd_dev) { struct rbd_snap *snap; struct rbd_snap *next; list_for_each_entry_safe(snap, next, &rbd_dev->snaps, node) - rbd_remove_snap_dev(snap); -} - -static void rbd_update_mapping_size(struct rbd_device *rbd_dev) -{ - sector_t size; - - if (rbd_dev->spec->snap_id != CEPH_NOSNAP) - return; - - size = (sector_t) rbd_dev->header.image_size / SECTOR_SIZE; - dout("setting size to %llu sectors", (unsigned long long) size); - rbd_dev->mapping.size = (u64) size; - set_capacity(rbd_dev->disk, size); + __rbd_remove_snap_dev(snap); } /* * only read the first part of the ondisk header, without the snaps info */ -static int rbd_dev_v1_refresh(struct rbd_device *rbd_dev, u64 *hver) +static int __rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) { int ret; struct rbd_image_header h; @@ -1832,9 +1730,17 @@ static int rbd_dev_v1_refresh(struct rbd_device *rbd_dev, u64 *hver) down_write(&rbd_dev->header_rwsem); - /* Update image size, and check for resize of mapped image */ - rbd_dev->header.image_size = h.image_size; - rbd_update_mapping_size(rbd_dev); + /* resized? */ + if (rbd_dev->mapping.snap_id == CEPH_NOSNAP) { + sector_t size = (sector_t) h.image_size / SECTOR_SIZE; + + if (size != (sector_t) rbd_dev->mapping.size) { + dout("setting size to %llu sectors", + (unsigned long long) size); + rbd_dev->mapping.size = (u64) size; + set_capacity(rbd_dev->disk, size); + } + } /* rbd_dev->header.object_prefix shouldn't change */ kfree(rbd_dev->header.snap_sizes); @@ -1862,16 +1768,12 @@ static int rbd_dev_v1_refresh(struct rbd_device *rbd_dev, u64 *hver) return ret; } -static int rbd_dev_refresh(struct rbd_device *rbd_dev, u64 *hver) +static int rbd_refresh_header(struct rbd_device *rbd_dev, u64 *hver) { int ret; - rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); mutex_lock_nested(&ctl_mutex, SINGLE_DEPTH_NESTING); - if (rbd_dev->image_format == 1) - ret = rbd_dev_v1_refresh(rbd_dev, hver); - else - ret = rbd_dev_v2_refresh(rbd_dev, hver); + ret = __rbd_refresh_header(rbd_dev, hver); mutex_unlock(&ctl_mutex); return ret; @@ -1983,7 +1885,7 @@ static ssize_t rbd_pool_show(struct device *dev, { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%s\n", rbd_dev->spec->pool_name); + return sprintf(buf, "%s\n", rbd_dev->pool_name); } static ssize_t rbd_pool_id_show(struct device *dev, @@ -1991,8 +1893,7 @@ static ssize_t rbd_pool_id_show(struct device *dev, { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%llu\n", - (unsigned long long) rbd_dev->spec->pool_id); + return sprintf(buf, "%d\n", rbd_dev->pool_id); } static ssize_t rbd_name_show(struct device *dev, @@ -2000,10 +1901,7 @@ static ssize_t rbd_name_show(struct device *dev, { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - if (rbd_dev->spec->image_name) - return sprintf(buf, "%s\n", rbd_dev->spec->image_name); - - return sprintf(buf, "(unknown)\n"); + return sprintf(buf, "%s\n", rbd_dev->image_name); } static ssize_t rbd_image_id_show(struct device *dev, @@ -2011,7 +1909,7 @@ static ssize_t rbd_image_id_show(struct device *dev, { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%s\n", rbd_dev->spec->image_id); + return sprintf(buf, "%s\n", rbd_dev->image_id); } /* @@ -2024,50 +1922,7 @@ static ssize_t rbd_snap_show(struct device *dev, { struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - return sprintf(buf, "%s\n", rbd_dev->spec->snap_name); -} - -/* - * For an rbd v2 image, shows the pool id, image id, and snapshot id - * for the parent image. If there is no parent, simply shows - * "(no parent image)". - */ -static ssize_t rbd_parent_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); - struct rbd_spec *spec = rbd_dev->parent_spec; - int count; - char *bufp = buf; - - if (!spec) - return sprintf(buf, "(no parent image)\n"); - - count = sprintf(bufp, "pool_id %llu\npool_name %s\n", - (unsigned long long) spec->pool_id, spec->pool_name); - if (count < 0) - return count; - bufp += count; - - count = sprintf(bufp, "image_id %s\nimage_name %s\n", spec->image_id, - spec->image_name ? spec->image_name : "(unknown)"); - if (count < 0) - return count; - bufp += count; - - count = sprintf(bufp, "snap_id %llu\nsnap_name %s\n", - (unsigned long long) spec->snap_id, spec->snap_name); - if (count < 0) - return count; - bufp += count; - - count = sprintf(bufp, "overlap %llu\n", rbd_dev->parent_overlap); - if (count < 0) - return count; - bufp += count; - - return (ssize_t) (bufp - buf); + return sprintf(buf, "%s\n", rbd_dev->mapping.snap_name); } static ssize_t rbd_image_refresh(struct device *dev, @@ -2078,7 +1933,7 @@ static ssize_t rbd_image_refresh(struct device *dev, struct rbd_device *rbd_dev = dev_to_rbd_dev(dev); int ret; - ret = rbd_dev_refresh(rbd_dev, NULL); + ret = rbd_refresh_header(rbd_dev, NULL); return ret < 0 ? ret : size; } @@ -2093,7 +1948,6 @@ static DEVICE_ATTR(name, S_IRUGO, rbd_name_show, NULL); static DEVICE_ATTR(image_id, S_IRUGO, rbd_image_id_show, NULL); static DEVICE_ATTR(refresh, S_IWUSR, NULL, rbd_image_refresh); static DEVICE_ATTR(current_snap, S_IRUGO, rbd_snap_show, NULL); -static DEVICE_ATTR(parent, S_IRUGO, rbd_parent_show, NULL); static struct attribute *rbd_attrs[] = { &dev_attr_size.attr, @@ -2105,7 +1959,6 @@ static struct attribute *rbd_attrs[] = { &dev_attr_name.attr, &dev_attr_image_id.attr, &dev_attr_current_snap.attr, - &dev_attr_parent.attr, &dev_attr_refresh.attr, NULL }; @@ -2194,74 +2047,6 @@ static struct device_type rbd_snap_device_type = { .release = rbd_snap_dev_release, }; -static struct rbd_spec *rbd_spec_get(struct rbd_spec *spec) -{ - kref_get(&spec->kref); - - return spec; -} - -static void rbd_spec_free(struct kref *kref); -static void rbd_spec_put(struct rbd_spec *spec) -{ - if (spec) - kref_put(&spec->kref, rbd_spec_free); -} - -static struct rbd_spec *rbd_spec_alloc(void) -{ - struct rbd_spec *spec; - - spec = kzalloc(sizeof (*spec), GFP_KERNEL); - if (!spec) - return NULL; - kref_init(&spec->kref); - - rbd_spec_put(rbd_spec_get(spec)); /* TEMPORARY */ - - return spec; -} - -static void rbd_spec_free(struct kref *kref) -{ - struct rbd_spec *spec = container_of(kref, struct rbd_spec, kref); - - kfree(spec->pool_name); - kfree(spec->image_id); - kfree(spec->image_name); - kfree(spec->snap_name); - kfree(spec); -} - -struct rbd_device *rbd_dev_create(struct rbd_client *rbdc, - struct rbd_spec *spec) -{ - struct rbd_device *rbd_dev; - - rbd_dev = kzalloc(sizeof (*rbd_dev), GFP_KERNEL); - if (!rbd_dev) - return NULL; - - spin_lock_init(&rbd_dev->lock); - INIT_LIST_HEAD(&rbd_dev->node); - INIT_LIST_HEAD(&rbd_dev->snaps); - init_rwsem(&rbd_dev->header_rwsem); - - rbd_dev->spec = spec; - rbd_dev->rbd_client = rbdc; - - return rbd_dev; -} - -static void rbd_dev_destroy(struct rbd_device *rbd_dev) -{ - rbd_spec_put(rbd_dev->parent_spec); - kfree(rbd_dev->header_name); - rbd_put_client(rbd_dev->rbd_client); - rbd_spec_put(rbd_dev->spec); - kfree(rbd_dev); -} - static bool rbd_snap_registered(struct rbd_snap *snap) { bool ret = snap->dev.type == &rbd_snap_device_type; @@ -2272,7 +2057,7 @@ static bool rbd_snap_registered(struct rbd_snap *snap) return ret; } -static void rbd_remove_snap_dev(struct rbd_snap *snap) +static void __rbd_remove_snap_dev(struct rbd_snap *snap) { list_del(&snap->node); if (device_is_registered(&snap->dev)) @@ -2288,7 +2073,7 @@ static int rbd_register_snap_dev(struct rbd_snap *snap, dev->type = &rbd_snap_device_type; dev->parent = parent; dev->release = rbd_snap_dev_release; - dev_set_name(dev, "%s%s", RBD_SNAP_DEV_NAME_PREFIX, snap->name); + dev_set_name(dev, "snap_%s", snap->name); dout("%s: registering device for snapshot %s\n", __func__, snap->name); ret = device_register(dev); @@ -2404,7 +2189,6 @@ static int rbd_dev_v2_object_prefix(struct rbd_device *rbd_dev) dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ p = reply_buf; rbd_dev->header.object_prefix = ceph_extract_encoded_string(&p, @@ -2432,7 +2216,6 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, __le64 features; __le64 incompat; } features_buf = { 0 }; - u64 incompat; int ret; ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, @@ -2443,11 +2226,6 @@ static int _rbd_dev_v2_snap_features(struct rbd_device *rbd_dev, u64 snap_id, dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) return ret; - - incompat = le64_to_cpu(features_buf.incompat); - if (incompat & ~RBD_FEATURES_ALL) - return -ENXIO; - *snap_features = le64_to_cpu(features_buf.features); dout(" snap_id 0x%016llx features = 0x%016llx incompat = 0x%016llx\n", @@ -2464,183 +2242,6 @@ static int rbd_dev_v2_features(struct rbd_device *rbd_dev) &rbd_dev->header.features); } -static int rbd_dev_v2_parent_info(struct rbd_device *rbd_dev) -{ - struct rbd_spec *parent_spec; - size_t size; - void *reply_buf = NULL; - __le64 snapid; - void *p; - void *end; - char *image_id; - u64 overlap; - size_t len = 0; - int ret; - - parent_spec = rbd_spec_alloc(); - if (!parent_spec) - return -ENOMEM; - - size = sizeof (__le64) + /* pool_id */ - sizeof (__le32) + RBD_IMAGE_ID_LEN_MAX + /* image_id */ - sizeof (__le64) + /* snap_id */ - sizeof (__le64); /* overlap */ - reply_buf = kmalloc(size, GFP_KERNEL); - if (!reply_buf) { - ret = -ENOMEM; - goto out_err; - } - - snapid = cpu_to_le64(CEPH_NOSNAP); - ret = rbd_req_sync_exec(rbd_dev, rbd_dev->header_name, - "rbd", "get_parent", - (char *) &snapid, sizeof (snapid), - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); - dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); - if (ret < 0) - goto out_err; - - ret = -ERANGE; - p = reply_buf; - end = (char *) reply_buf + size; - ceph_decode_64_safe(&p, end, parent_spec->pool_id, out_err); - if (parent_spec->pool_id == CEPH_NOPOOL) - goto out; /* No parent? No problem. */ - - image_id = ceph_extract_encoded_string(&p, end, &len, GFP_KERNEL); - if (IS_ERR(image_id)) { - ret = PTR_ERR(image_id); - goto out_err; - } - parent_spec->image_id = image_id; - parent_spec->image_id_len = len; - ceph_decode_64_safe(&p, end, parent_spec->snap_id, out_err); - ceph_decode_64_safe(&p, end, overlap, out_err); - - rbd_dev->parent_overlap = overlap; - rbd_dev->parent_spec = parent_spec; - parent_spec = NULL; /* rbd_dev now owns this */ -out: - ret = 0; -out_err: - kfree(reply_buf); - rbd_spec_put(parent_spec); - - return ret; -} - -static char *rbd_dev_image_name(struct rbd_device *rbd_dev) -{ - size_t image_id_size; - char *image_id; - void *p; - void *end; - size_t size; - void *reply_buf = NULL; - size_t len = 0; - char *image_name = NULL; - int ret; - - rbd_assert(!rbd_dev->spec->image_name); - - image_id_size = sizeof (__le32) + rbd_dev->spec->image_id_len; - image_id = kmalloc(image_id_size, GFP_KERNEL); - if (!image_id) - return NULL; - - p = image_id; - end = (char *) image_id + image_id_size; - ceph_encode_string(&p, end, rbd_dev->spec->image_id, - (u32) rbd_dev->spec->image_id_len); - - size = sizeof (__le32) + RBD_IMAGE_NAME_LEN_MAX; - reply_buf = kmalloc(size, GFP_KERNEL); - if (!reply_buf) - goto out; - - ret = rbd_req_sync_exec(rbd_dev, RBD_DIRECTORY, - "rbd", "dir_get_name", - image_id, image_id_size, - (char *) reply_buf, size, - CEPH_OSD_FLAG_READ, NULL); - if (ret < 0) - goto out; - p = reply_buf; - end = (char *) reply_buf + size; - image_name = ceph_extract_encoded_string(&p, end, &len, GFP_KERNEL); - if (IS_ERR(image_name)) - image_name = NULL; - else - dout("%s: name is %s len is %zd\n", __func__, image_name, len); -out: - kfree(reply_buf); - kfree(image_id); - - return image_name; -} - -/* - * When a parent image gets probed, we only have the pool, image, - * and snapshot ids but not the names of any of them. This call - * is made later to fill in those names. It has to be done after - * rbd_dev_snaps_update() has completed because some of the - * information (in particular, snapshot name) is not available - * until then. - */ -static int rbd_dev_probe_update_spec(struct rbd_device *rbd_dev) -{ - struct ceph_osd_client *osdc; - const char *name; - void *reply_buf = NULL; - int ret; - - if (rbd_dev->spec->pool_name) - return 0; /* Already have the names */ - - /* Look up the pool name */ - - osdc = &rbd_dev->rbd_client->client->osdc; - name = ceph_pg_pool_name_by_id(osdc->osdmap, rbd_dev->spec->pool_id); - if (!name) - return -EIO; /* pool id too large (>= 2^31) */ - - rbd_dev->spec->pool_name = kstrdup(name, GFP_KERNEL); - if (!rbd_dev->spec->pool_name) - return -ENOMEM; - - /* Fetch the image name; tolerate failure here */ - - name = rbd_dev_image_name(rbd_dev); - if (name) { - rbd_dev->spec->image_name_len = strlen(name); - rbd_dev->spec->image_name = (char *) name; - } else { - pr_warning(RBD_DRV_NAME "%d " - "unable to get image name for image id %s\n", - rbd_dev->major, rbd_dev->spec->image_id); - } - - /* Look up the snapshot name. */ - - name = rbd_snap_name(rbd_dev, rbd_dev->spec->snap_id); - if (!name) { - ret = -EIO; - goto out_err; - } - rbd_dev->spec->snap_name = kstrdup(name, GFP_KERNEL); - if(!rbd_dev->spec->snap_name) - goto out_err; - - return 0; -out_err: - kfree(reply_buf); - kfree(rbd_dev->spec->pool_name); - rbd_dev->spec->pool_name = NULL; - - return ret; -} - static int rbd_dev_v2_snap_context(struct rbd_device *rbd_dev, u64 *ver) { size_t size; @@ -2727,6 +2328,7 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) int ret; void *p; void *end; + size_t snap_name_len; char *snap_name; size = sizeof (__le32) + RBD_MAX_SNAP_NAME_LEN; @@ -2746,7 +2348,9 @@ static char *rbd_dev_v2_snap_name(struct rbd_device *rbd_dev, u32 which) p = reply_buf; end = (char *) reply_buf + size; - snap_name = ceph_extract_encoded_string(&p, end, NULL, GFP_KERNEL); + snap_name_len = 0; + snap_name = ceph_extract_encoded_string(&p, end, &snap_name_len, + GFP_KERNEL); if (IS_ERR(snap_name)) { ret = PTR_ERR(snap_name); goto out; @@ -2793,41 +2397,6 @@ static char *rbd_dev_snap_info(struct rbd_device *rbd_dev, u32 which, return ERR_PTR(-EINVAL); } -static int rbd_dev_v2_refresh(struct rbd_device *rbd_dev, u64 *hver) -{ - int ret; - __u8 obj_order; - - down_write(&rbd_dev->header_rwsem); - - /* Grab old order first, to see if it changes */ - - obj_order = rbd_dev->header.obj_order, - ret = rbd_dev_v2_image_size(rbd_dev); - if (ret) - goto out; - if (rbd_dev->header.obj_order != obj_order) { - ret = -EIO; - goto out; - } - rbd_update_mapping_size(rbd_dev); - - ret = rbd_dev_v2_snap_context(rbd_dev, hver); - dout("rbd_dev_v2_snap_context returned %d\n", ret); - if (ret) - goto out; - ret = rbd_dev_snaps_update(rbd_dev); - dout("rbd_dev_snaps_update returned %d\n", ret); - if (ret) - goto out; - ret = rbd_dev_snaps_register(rbd_dev); - dout("rbd_dev_snaps_register returned %d\n", ret); -out: - up_write(&rbd_dev->header_rwsem); - - return ret; -} - /* * Scan the rbd device's current snapshot list and compare it to the * newly-received snapshot context. Remove any existing snapshots @@ -2867,12 +2436,12 @@ static int rbd_dev_snaps_update(struct rbd_device *rbd_dev) /* Existing snapshot not in the new snap context */ - if (rbd_dev->spec->snap_id == snap->id) - rbd_dev->exists = false; - rbd_remove_snap_dev(snap); + if (rbd_dev->mapping.snap_id == snap->id) + rbd_dev->mapping.snap_exists = false; + __rbd_remove_snap_dev(snap); dout("%ssnap id %llu has been removed\n", - rbd_dev->spec->snap_id == snap->id ? - "mapped " : "", + rbd_dev->mapping.snap_id == snap->id ? + "mapped " : "", (unsigned long long) snap->id); /* Done with this list entry; advance */ @@ -2990,7 +2559,7 @@ static int rbd_init_watch_dev(struct rbd_device *rbd_dev) do { ret = rbd_req_sync_watch(rbd_dev); if (ret == -ERANGE) { - rc = rbd_dev_refresh(rbd_dev, NULL); + rc = rbd_refresh_header(rbd_dev, NULL); if (rc < 0) return rc; } @@ -3052,8 +2621,8 @@ static void rbd_dev_id_put(struct rbd_device *rbd_dev) struct rbd_device *rbd_dev; rbd_dev = list_entry(tmp, struct rbd_device, node); - if (rbd_dev->dev_id > max_id) - max_id = rbd_dev->dev_id; + if (rbd_id > max_id) + max_id = rbd_id; } spin_unlock(&rbd_dev_list_lock); @@ -3153,140 +2722,73 @@ static inline char *dup_token(const char **buf, size_t *lenp) } /* - * Parse the options provided for an "rbd add" (i.e., rbd image - * mapping) request. These arrive via a write to /sys/bus/rbd/add, - * and the data written is passed here via a NUL-terminated buffer. - * Returns 0 if successful or an error code otherwise. - * - * The information extracted from these options is recorded in - * the other parameters which return dynamically-allocated - * structures: - * ceph_opts - * The address of a pointer that will refer to a ceph options - * structure. Caller must release the returned pointer using - * ceph_destroy_options() when it is no longer needed. - * rbd_opts - * Address of an rbd options pointer. Fully initialized by - * this function; caller must release with kfree(). - * spec - * Address of an rbd image specification pointer. Fully - * initialized by this function based on parsed options. - * Caller must release with rbd_spec_put(). + * This fills in the pool_name, image_name, image_name_len, rbd_dev, + * rbd_md_name, and name fields of the given rbd_dev, based on the + * list of monitor addresses and other options provided via + * /sys/bus/rbd/add. Returns a pointer to a dynamically-allocated + * copy of the snapshot name to map if successful, or a + * pointer-coded error otherwise. * - * The options passed take this form: - * [] - * where: - * - * A comma-separated list of one or more monitor addresses. - * A monitor address is an ip address, optionally followed - * by a port number (separated by a colon). - * I.e.: ip1[:port1][,ip2[:port2]...] - * - * A comma-separated list of ceph and/or rbd options. - * - * The name of the rados pool containing the rbd image. - * - * The name of the image in that pool to map. - * - * An optional snapshot id. If provided, the mapping will - * present data from the image at the time that snapshot was - * created. The image head is used if no snapshot id is - * provided. Snapshot mappings are always read-only. + * Note: rbd_dev is assumed to have been initially zero-filled. */ -static int rbd_add_parse_args(const char *buf, - struct ceph_options **ceph_opts, - struct rbd_options **opts, - struct rbd_spec **rbd_spec) +static char *rbd_add_parse_args(struct rbd_device *rbd_dev, + const char *buf, + const char **mon_addrs, + size_t *mon_addrs_size, + char *options, + size_t options_size) { size_t len; - char *options; - const char *mon_addrs; - size_t mon_addrs_size; - struct rbd_spec *spec = NULL; - struct rbd_options *rbd_opts = NULL; - struct ceph_options *copts; - int ret; + char *err_ptr = ERR_PTR(-EINVAL); + char *snap_name; /* The first four tokens are required */ len = next_token(&buf); if (!len) - return -EINVAL; /* Missing monitor address(es) */ - mon_addrs = buf; - mon_addrs_size = len + 1; - buf += len; + return err_ptr; + *mon_addrs_size = len + 1; + *mon_addrs = buf; - ret = -EINVAL; - options = dup_token(&buf, NULL); - if (!options) - return -ENOMEM; - if (!*options) - goto out_err; /* Missing options */ + buf += len; - spec = rbd_spec_alloc(); - if (!spec) - goto out_mem; + len = copy_token(&buf, options, options_size); + if (!len || len >= options_size) + return err_ptr; - spec->pool_name = dup_token(&buf, NULL); - if (!spec->pool_name) - goto out_mem; - if (!*spec->pool_name) - goto out_err; /* Missing pool name */ + err_ptr = ERR_PTR(-ENOMEM); + rbd_dev->pool_name = dup_token(&buf, NULL); + if (!rbd_dev->pool_name) + goto out_err; - spec->image_name = dup_token(&buf, &spec->image_name_len); - if (!spec->image_name) - goto out_mem; - if (!*spec->image_name) - goto out_err; /* Missing image name */ + rbd_dev->image_name = dup_token(&buf, &rbd_dev->image_name_len); + if (!rbd_dev->image_name) + goto out_err; - /* - * Snapshot name is optional; default is to use "-" - * (indicating the head/no snapshot). - */ + /* Snapshot name is optional */ len = next_token(&buf); if (!len) { buf = RBD_SNAP_HEAD_NAME; /* No snapshot supplied */ len = sizeof (RBD_SNAP_HEAD_NAME) - 1; - } else if (len > RBD_MAX_SNAP_NAME_LEN) { - ret = -ENAMETOOLONG; - goto out_err; } - spec->snap_name = kmalloc(len + 1, GFP_KERNEL); - if (!spec->snap_name) - goto out_mem; - memcpy(spec->snap_name, buf, len); - *(spec->snap_name + len) = '\0'; - - /* Initialize all rbd options to the defaults */ - - rbd_opts = kzalloc(sizeof (*rbd_opts), GFP_KERNEL); - if (!rbd_opts) - goto out_mem; - - rbd_opts->read_only = RBD_READ_ONLY_DEFAULT; - - copts = ceph_parse_options(options, mon_addrs, - mon_addrs + mon_addrs_size - 1, - parse_rbd_opts_token, rbd_opts); - if (IS_ERR(copts)) { - ret = PTR_ERR(copts); + snap_name = kmalloc(len + 1, GFP_KERNEL); + if (!snap_name) goto out_err; - } - kfree(options); + memcpy(snap_name, buf, len); + *(snap_name + len) = '\0'; - *ceph_opts = copts; - *opts = rbd_opts; - *rbd_spec = spec; +dout(" SNAP_NAME is <%s>, len is %zd\n", snap_name, len); + + return snap_name; - return 0; -out_mem: - ret = -ENOMEM; out_err: - kfree(rbd_opts); - rbd_spec_put(spec); - kfree(options); + kfree(rbd_dev->image_name); + rbd_dev->image_name = NULL; + rbd_dev->image_name_len = 0; + kfree(rbd_dev->pool_name); + rbd_dev->pool_name = NULL; - return ret; + return err_ptr; } /* @@ -3311,23 +2813,15 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) void *response; void *p; - /* - * When probing a parent image, the image id is already - * known (and the image name likely is not). There's no - * need to fetch the image id again in this case. - */ - if (rbd_dev->spec->image_id) - return 0; - /* * First, see if the format 2 image id file exists, and if * so, get the image's persistent id from it. */ - size = sizeof (RBD_ID_PREFIX) + rbd_dev->spec->image_name_len; + size = sizeof (RBD_ID_PREFIX) + rbd_dev->image_name_len; object_name = kmalloc(size, GFP_NOIO); if (!object_name) return -ENOMEM; - sprintf(object_name, "%s%s", RBD_ID_PREFIX, rbd_dev->spec->image_name); + sprintf(object_name, "%s%s", RBD_ID_PREFIX, rbd_dev->image_name); dout("rbd id object name is %s\n", object_name); /* Response will be an encoded string, which includes a length */ @@ -3347,18 +2841,17 @@ static int rbd_dev_image_id(struct rbd_device *rbd_dev) dout("%s: rbd_req_sync_exec returned %d\n", __func__, ret); if (ret < 0) goto out; - ret = 0; /* rbd_req_sync_exec() can return positive */ p = response; - rbd_dev->spec->image_id = ceph_extract_encoded_string(&p, + rbd_dev->image_id = ceph_extract_encoded_string(&p, p + RBD_IMAGE_ID_LEN_MAX, - &rbd_dev->spec->image_id_len, + &rbd_dev->image_id_len, GFP_NOIO); - if (IS_ERR(rbd_dev->spec->image_id)) { - ret = PTR_ERR(rbd_dev->spec->image_id); - rbd_dev->spec->image_id = NULL; + if (IS_ERR(rbd_dev->image_id)) { + ret = PTR_ERR(rbd_dev->image_id); + rbd_dev->image_id = NULL; } else { - dout("image_id is %s\n", rbd_dev->spec->image_id); + dout("image_id is %s\n", rbd_dev->image_id); } out: kfree(response); @@ -3374,33 +2867,26 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) /* Version 1 images have no id; empty string is used */ - rbd_dev->spec->image_id = kstrdup("", GFP_KERNEL); - if (!rbd_dev->spec->image_id) + rbd_dev->image_id = kstrdup("", GFP_KERNEL); + if (!rbd_dev->image_id) return -ENOMEM; - rbd_dev->spec->image_id_len = 0; + rbd_dev->image_id_len = 0; /* Record the header object name for this rbd image. */ - size = rbd_dev->spec->image_name_len + sizeof (RBD_SUFFIX); + size = rbd_dev->image_name_len + sizeof (RBD_SUFFIX); rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) { ret = -ENOMEM; goto out_err; } - sprintf(rbd_dev->header_name, "%s%s", - rbd_dev->spec->image_name, RBD_SUFFIX); + sprintf(rbd_dev->header_name, "%s%s", rbd_dev->image_name, RBD_SUFFIX); /* Populate rbd image metadata */ ret = rbd_read_header(rbd_dev, &rbd_dev->header); if (ret < 0) goto out_err; - - /* Version 1 images have no parent (no layering) */ - - rbd_dev->parent_spec = NULL; - rbd_dev->parent_overlap = 0; - rbd_dev->image_format = 1; dout("discovered version 1 image, header name is %s\n", @@ -3411,8 +2897,8 @@ static int rbd_dev_v1_probe(struct rbd_device *rbd_dev) out_err: kfree(rbd_dev->header_name); rbd_dev->header_name = NULL; - kfree(rbd_dev->spec->image_id); - rbd_dev->spec->image_id = NULL; + kfree(rbd_dev->image_id); + rbd_dev->image_id = NULL; return ret; } @@ -3427,12 +2913,12 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) * Image id was filled in by the caller. Record the header * object name for this rbd image. */ - size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->spec->image_id_len; + size = sizeof (RBD_HEADER_PREFIX) + rbd_dev->image_id_len; rbd_dev->header_name = kmalloc(size, GFP_KERNEL); if (!rbd_dev->header_name) return -ENOMEM; sprintf(rbd_dev->header_name, "%s%s", - RBD_HEADER_PREFIX, rbd_dev->spec->image_id); + RBD_HEADER_PREFIX, rbd_dev->image_id); /* Get the size and object order for the image */ @@ -3446,20 +2932,12 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) if (ret < 0) goto out_err; - /* Get the and check features for the image */ + /* Get the features for the image */ ret = rbd_dev_v2_features(rbd_dev); if (ret < 0) goto out_err; - /* If the image supports layering, get the parent info */ - - if (rbd_dev->header.features & RBD_FEATURE_LAYERING) { - ret = rbd_dev_v2_parent_info(rbd_dev); - if (ret < 0) - goto out_err; - } - /* crypto and compression type aren't (yet) supported for v2 images */ rbd_dev->header.crypt_type = 0; @@ -3477,11 +2955,8 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) dout("discovered version 2 image, header name is %s\n", rbd_dev->header_name); - return 0; + return -ENOTSUPP; out_err: - rbd_dev->parent_overlap = 0; - rbd_spec_put(rbd_dev->parent_spec); - rbd_dev->parent_spec = NULL; kfree(rbd_dev->header_name); rbd_dev->header_name = NULL; kfree(rbd_dev->header.object_prefix); @@ -3490,22 +2965,91 @@ static int rbd_dev_v2_probe(struct rbd_device *rbd_dev) return ret; } -static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) +/* + * Probe for the existence of the header object for the given rbd + * device. For format 2 images this includes determining the image + * id. + */ +static int rbd_dev_probe(struct rbd_device *rbd_dev) { int ret; - /* no need to lock here, as rbd_dev is not registered yet */ - ret = rbd_dev_snaps_update(rbd_dev); + /* + * Get the id from the image id object. If it's not a + * format 2 image, we'll get ENOENT back, and we'll assume + * it's a format 1 image. + */ + ret = rbd_dev_image_id(rbd_dev); if (ret) - return ret; - - ret = rbd_dev_probe_update_spec(rbd_dev); + ret = rbd_dev_v1_probe(rbd_dev); + else + ret = rbd_dev_v2_probe(rbd_dev); if (ret) - goto err_out_snaps; + dout("probe failed, returning %d\n", ret); - ret = rbd_dev_set_mapping(rbd_dev); - if (ret) - goto err_out_snaps; + return ret; +} + +static ssize_t rbd_add(struct bus_type *bus, + const char *buf, + size_t count) +{ + char *options; + struct rbd_device *rbd_dev = NULL; + const char *mon_addrs = NULL; + size_t mon_addrs_size = 0; + struct ceph_osd_client *osdc; + int rc = -ENOMEM; + char *snap_name; + + if (!try_module_get(THIS_MODULE)) + return -ENODEV; + + options = kmalloc(count, GFP_KERNEL); + if (!options) + goto err_out_mem; + rbd_dev = kzalloc(sizeof(*rbd_dev), GFP_KERNEL); + if (!rbd_dev) + goto err_out_mem; + + /* static rbd_device initialization */ + spin_lock_init(&rbd_dev->lock); + INIT_LIST_HEAD(&rbd_dev->node); + INIT_LIST_HEAD(&rbd_dev->snaps); + init_rwsem(&rbd_dev->header_rwsem); + + /* parse add command */ + snap_name = rbd_add_parse_args(rbd_dev, buf, + &mon_addrs, &mon_addrs_size, options, count); + if (IS_ERR(snap_name)) { + rc = PTR_ERR(snap_name); + goto err_out_mem; + } + + rc = rbd_get_client(rbd_dev, mon_addrs, mon_addrs_size - 1, options); + if (rc < 0) + goto err_out_args; + + /* pick the pool */ + osdc = &rbd_dev->rbd_client->client->osdc; + rc = ceph_pg_poolid_by_name(osdc->osdmap, rbd_dev->pool_name); + if (rc < 0) + goto err_out_client; + rbd_dev->pool_id = rc; + + rc = rbd_dev_probe(rbd_dev); + if (rc < 0) + goto err_out_client; + rbd_assert(rbd_image_format_valid(rbd_dev->image_format)); + + /* no need to lock here, as rbd_dev is not registered yet */ + rc = rbd_dev_snaps_update(rbd_dev); + if (rc) + goto err_out_header; + + rc = rbd_dev_set_mapping(rbd_dev, snap_name); + if (rc) + goto err_out_header; /* generate unique id: find highest unique id, add one */ rbd_dev_id_get(rbd_dev); @@ -3517,33 +3061,34 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) /* Get our block major device number. */ - ret = register_blkdev(0, rbd_dev->name); - if (ret < 0) + rc = register_blkdev(0, rbd_dev->name); + if (rc < 0) goto err_out_id; - rbd_dev->major = ret; + rbd_dev->major = rc; /* Set up the blkdev mapping. */ - ret = rbd_init_disk(rbd_dev); - if (ret) + rc = rbd_init_disk(rbd_dev); + if (rc) goto err_out_blkdev; - ret = rbd_bus_add_dev(rbd_dev); - if (ret) + rc = rbd_bus_add_dev(rbd_dev); + if (rc) goto err_out_disk; /* * At this point cleanup in the event of an error is the job * of the sysfs code (initiated by rbd_bus_del_dev()). */ + down_write(&rbd_dev->header_rwsem); - ret = rbd_dev_snaps_register(rbd_dev); + rc = rbd_dev_snaps_register(rbd_dev); up_write(&rbd_dev->header_rwsem); - if (ret) + if (rc) goto err_out_bus; - ret = rbd_init_watch_dev(rbd_dev); - if (ret) + rc = rbd_init_watch_dev(rbd_dev); + if (rc) goto err_out_bus; /* Everything's ready. Announce the disk to the world. */ @@ -3553,119 +3098,37 @@ static int rbd_dev_probe_finish(struct rbd_device *rbd_dev) pr_info("%s: added with size 0x%llx\n", rbd_dev->disk->disk_name, (unsigned long long) rbd_dev->mapping.size); - return ret; + return count; + err_out_bus: /* this will also clean up rest of rbd_dev stuff */ rbd_bus_del_dev(rbd_dev); + kfree(options); + return rc; - return ret; err_out_disk: rbd_free_disk(rbd_dev); err_out_blkdev: unregister_blkdev(rbd_dev->major, rbd_dev->name); err_out_id: rbd_dev_id_put(rbd_dev); -err_out_snaps: - rbd_remove_all_snaps(rbd_dev); - - return ret; -} - -/* - * Probe for the existence of the header object for the given rbd - * device. For format 2 images this includes determining the image - * id. - */ -static int rbd_dev_probe(struct rbd_device *rbd_dev) -{ - int ret; - - /* - * Get the id from the image id object. If it's not a - * format 2 image, we'll get ENOENT back, and we'll assume - * it's a format 1 image. - */ - ret = rbd_dev_image_id(rbd_dev); - if (ret) - ret = rbd_dev_v1_probe(rbd_dev); - else - ret = rbd_dev_v2_probe(rbd_dev); - if (ret) { - dout("probe failed, returning %d\n", ret); - - return ret; - } - - ret = rbd_dev_probe_finish(rbd_dev); - if (ret) - rbd_header_free(&rbd_dev->header); - - return ret; -} - -static ssize_t rbd_add(struct bus_type *bus, - const char *buf, - size_t count) -{ - struct rbd_device *rbd_dev = NULL; - struct ceph_options *ceph_opts = NULL; - struct rbd_options *rbd_opts = NULL; - struct rbd_spec *spec = NULL; - struct rbd_client *rbdc; - struct ceph_osd_client *osdc; - int rc = -ENOMEM; - - if (!try_module_get(THIS_MODULE)) - return -ENODEV; - - /* parse add command */ - rc = rbd_add_parse_args(buf, &ceph_opts, &rbd_opts, &spec); - if (rc < 0) - goto err_out_module; - - rbdc = rbd_get_client(ceph_opts); - if (IS_ERR(rbdc)) { - rc = PTR_ERR(rbdc); - goto err_out_args; - } - ceph_opts = NULL; /* rbd_dev client now owns this */ - - /* pick the pool */ - osdc = &rbdc->client->osdc; - rc = ceph_pg_poolid_by_name(osdc->osdmap, spec->pool_name); - if (rc < 0) - goto err_out_client; - spec->pool_id = (u64) rc; - - rbd_dev = rbd_dev_create(rbdc, spec); - if (!rbd_dev) - goto err_out_client; - rbdc = NULL; /* rbd_dev now owns this */ - spec = NULL; /* rbd_dev now owns this */ - - rbd_dev->mapping.read_only = rbd_opts->read_only; - kfree(rbd_opts); - rbd_opts = NULL; /* done with this */ - - rc = rbd_dev_probe(rbd_dev); - if (rc < 0) - goto err_out_rbd_dev; - - return count; -err_out_rbd_dev: - rbd_dev_destroy(rbd_dev); +err_out_header: + rbd_header_free(&rbd_dev->header); err_out_client: - rbd_put_client(rbdc); + kfree(rbd_dev->header_name); + rbd_put_client(rbd_dev); + kfree(rbd_dev->image_id); err_out_args: - if (ceph_opts) - ceph_destroy_options(ceph_opts); - kfree(rbd_opts); - rbd_spec_put(spec); -err_out_module: - module_put(THIS_MODULE); + kfree(rbd_dev->mapping.snap_name); + kfree(rbd_dev->image_name); + kfree(rbd_dev->pool_name); +err_out_mem: + kfree(rbd_dev); + kfree(options); dout("Error adding device %s\n", buf); + module_put(THIS_MODULE); return (ssize_t) rc; } @@ -3700,6 +3163,7 @@ static void rbd_dev_release(struct device *dev) if (rbd_dev->watch_event) rbd_req_sync_unwatch(rbd_dev); + rbd_put_client(rbd_dev); /* clean up and free blkdev */ rbd_free_disk(rbd_dev); @@ -3709,9 +3173,13 @@ static void rbd_dev_release(struct device *dev) rbd_header_free(&rbd_dev->header); /* done with the id, and with the rbd_dev */ + kfree(rbd_dev->mapping.snap_name); + kfree(rbd_dev->image_id); + kfree(rbd_dev->header_name); + kfree(rbd_dev->pool_name); + kfree(rbd_dev->image_name); rbd_dev_id_put(rbd_dev); - rbd_assert(rbd_dev->rbd_client != NULL); - rbd_dev_destroy(rbd_dev); + kfree(rbd_dev); /* release module ref */ module_put(THIS_MODULE); @@ -3743,12 +3211,7 @@ static ssize_t rbd_remove(struct bus_type *bus, goto done; } - if (rbd_dev->open_count) { - ret = -EBUSY; - goto done; - } - - rbd_remove_all_snaps(rbd_dev); + __rbd_remove_all_snaps(rbd_dev); rbd_bus_del_dev(rbd_dev); done: diff --git a/trunk/drivers/block/rbd_types.h b/trunk/drivers/block/rbd_types.h index 49d77cbcf8bd..cbe77fa105ba 100644 --- a/trunk/drivers/block/rbd_types.h +++ b/trunk/drivers/block/rbd_types.h @@ -46,6 +46,8 @@ #define RBD_MIN_OBJ_ORDER 16 #define RBD_MAX_OBJ_ORDER 30 +#define RBD_MAX_SEG_NAME_LEN 128 + #define RBD_COMP_NONE 0 #define RBD_CRYPT_NONE 0 diff --git a/trunk/drivers/iommu/amd_iommu.c b/trunk/drivers/iommu/amd_iommu.c index c1c74e030a58..55074cba20eb 100644 --- a/trunk/drivers/iommu/amd_iommu.c +++ b/trunk/drivers/iommu/amd_iommu.c @@ -57,9 +57,17 @@ * physically contiguous memory regions it is mapping into page sizes * that we support. * - * 512GB Pages are not supported due to a hardware bug + * Traditionally the IOMMU core just handed us the mappings directly, + * after making sure the size is an order of a 4KiB page and that the + * mapping has natural alignment. + * + * To retain this behavior, we currently advertise that we support + * all page sizes that are an order of 4KiB. + * + * If at some point we'd like to utilize the IOMMU core's new behavior, + * we could change this to advertise the real page sizes we support. */ -#define AMD_IOMMU_PGSIZES ((~0xFFFUL) & ~(2ULL << 38)) +#define AMD_IOMMU_PGSIZES (~0xFFFUL) static DEFINE_RWLOCK(amd_iommu_devtable_lock); @@ -132,9 +140,6 @@ static void free_dev_data(struct iommu_dev_data *dev_data) list_del(&dev_data->dev_data_list); spin_unlock_irqrestore(&dev_data_list_lock, flags); - if (dev_data->group) - iommu_group_put(dev_data->group); - kfree(dev_data); } @@ -269,23 +274,41 @@ static void swap_pci_ref(struct pci_dev **from, struct pci_dev *to) *from = to; } -static struct pci_bus *find_hosted_bus(struct pci_bus *bus) +#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) + +static int iommu_init_device(struct device *dev) { - while (!bus->self) { - if (!pci_is_root_bus(bus)) - bus = bus->parent; - else - return ERR_PTR(-ENODEV); - } + struct pci_dev *dma_pdev = NULL, *pdev = to_pci_dev(dev); + struct iommu_dev_data *dev_data; + struct iommu_group *group; + u16 alias; + int ret; - return bus; -} + if (dev->archdata.iommu) + return 0; -#define REQ_ACS_FLAGS (PCI_ACS_SV | PCI_ACS_RR | PCI_ACS_CR | PCI_ACS_UF) + dev_data = find_dev_data(get_device_id(dev)); + if (!dev_data) + return -ENOMEM; -static struct pci_dev *get_isolation_root(struct pci_dev *pdev) -{ - struct pci_dev *dma_pdev = pdev; + alias = amd_iommu_alias_table[dev_data->devid]; + if (alias != dev_data->devid) { + struct iommu_dev_data *alias_data; + + alias_data = find_dev_data(alias); + if (alias_data == NULL) { + pr_err("AMD-Vi: Warning: Unhandled device %s\n", + dev_name(dev)); + free_dev_data(dev_data); + return -ENOTSUPP; + } + dev_data->alias_data = alias_data; + + dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); + } + + if (dma_pdev == NULL) + dma_pdev = pci_dev_get(pdev); /* Account for quirked devices */ swap_pci_ref(&dma_pdev, pci_get_dma_source(dma_pdev)); @@ -307,9 +330,14 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev) * Finding the next device may require skipping virtual buses. */ while (!pci_is_root_bus(dma_pdev->bus)) { - struct pci_bus *bus = find_hosted_bus(dma_pdev->bus); - if (IS_ERR(bus)) - break; + struct pci_bus *bus = dma_pdev->bus; + + while (!bus->self) { + if (!pci_is_root_bus(bus)) + bus = bus->parent; + else + goto root_bus; + } if (pci_acs_path_enabled(bus->self, NULL, REQ_ACS_FLAGS)) break; @@ -317,137 +345,19 @@ static struct pci_dev *get_isolation_root(struct pci_dev *pdev) swap_pci_ref(&dma_pdev, pci_dev_get(bus->self)); } - return dma_pdev; -} - -static int use_pdev_iommu_group(struct pci_dev *pdev, struct device *dev) -{ - struct iommu_group *group = iommu_group_get(&pdev->dev); - int ret; - +root_bus: + group = iommu_group_get(&dma_pdev->dev); + pci_dev_put(dma_pdev); if (!group) { group = iommu_group_alloc(); if (IS_ERR(group)) return PTR_ERR(group); - - WARN_ON(&pdev->dev != dev); } ret = iommu_group_add_device(group, dev); - iommu_group_put(group); - return ret; -} - -static int use_dev_data_iommu_group(struct iommu_dev_data *dev_data, - struct device *dev) -{ - if (!dev_data->group) { - struct iommu_group *group = iommu_group_alloc(); - if (IS_ERR(group)) - return PTR_ERR(group); - - dev_data->group = group; - } - - return iommu_group_add_device(dev_data->group, dev); -} -static int init_iommu_group(struct device *dev) -{ - struct iommu_dev_data *dev_data; - struct iommu_group *group; - struct pci_dev *dma_pdev; - int ret; - - group = iommu_group_get(dev); - if (group) { - iommu_group_put(group); - return 0; - } - - dev_data = find_dev_data(get_device_id(dev)); - if (!dev_data) - return -ENOMEM; - - if (dev_data->alias_data) { - u16 alias; - struct pci_bus *bus; - - if (dev_data->alias_data->group) - goto use_group; - - /* - * If the alias device exists, it's effectively just a first - * level quirk for finding the DMA source. - */ - alias = amd_iommu_alias_table[dev_data->devid]; - dma_pdev = pci_get_bus_and_slot(alias >> 8, alias & 0xff); - if (dma_pdev) { - dma_pdev = get_isolation_root(dma_pdev); - goto use_pdev; - } - - /* - * If the alias is virtual, try to find a parent device - * and test whether the IOMMU group is actualy rooted above - * the alias. Be careful to also test the parent device if - * we think the alias is the root of the group. - */ - bus = pci_find_bus(0, alias >> 8); - if (!bus) - goto use_group; - - bus = find_hosted_bus(bus); - if (IS_ERR(bus) || !bus->self) - goto use_group; - - dma_pdev = get_isolation_root(pci_dev_get(bus->self)); - if (dma_pdev != bus->self || (dma_pdev->multifunction && - !pci_acs_enabled(dma_pdev, REQ_ACS_FLAGS))) - goto use_pdev; - - pci_dev_put(dma_pdev); - goto use_group; - } - - dma_pdev = get_isolation_root(pci_dev_get(to_pci_dev(dev))); -use_pdev: - ret = use_pdev_iommu_group(dma_pdev, dev); - pci_dev_put(dma_pdev); - return ret; -use_group: - return use_dev_data_iommu_group(dev_data->alias_data, dev); -} - -static int iommu_init_device(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct iommu_dev_data *dev_data; - u16 alias; - int ret; - - if (dev->archdata.iommu) - return 0; - - dev_data = find_dev_data(get_device_id(dev)); - if (!dev_data) - return -ENOMEM; - - alias = amd_iommu_alias_table[dev_data->devid]; - if (alias != dev_data->devid) { - struct iommu_dev_data *alias_data; - - alias_data = find_dev_data(alias); - if (alias_data == NULL) { - pr_err("AMD-Vi: Warning: Unhandled device %s\n", - dev_name(dev)); - free_dev_data(dev_data); - return -ENOTSUPP; - } - dev_data->alias_data = alias_data; - } + iommu_group_put(group); - ret = init_iommu_group(dev); if (ret) return ret; diff --git a/trunk/drivers/iommu/amd_iommu_types.h b/trunk/drivers/iommu/amd_iommu_types.h index e38ab438bb34..c9aa3d079ff0 100644 --- a/trunk/drivers/iommu/amd_iommu_types.h +++ b/trunk/drivers/iommu/amd_iommu_types.h @@ -426,7 +426,6 @@ struct iommu_dev_data { struct iommu_dev_data *alias_data;/* The alias dev_data */ struct protection_domain *domain; /* Domain the device is bound to */ atomic_t bind; /* Domain attach reference count */ - struct iommu_group *group; /* IOMMU group for virtual aliases */ u16 devid; /* PCI Device ID */ bool iommu_v2; /* Device can make use of IOMMUv2 */ bool passthrough; /* Default for device is pt_domain */ diff --git a/trunk/drivers/iommu/intel-iommu.c b/trunk/drivers/iommu/intel-iommu.c index c2c07a4a7f21..0badfa48b32b 100644 --- a/trunk/drivers/iommu/intel-iommu.c +++ b/trunk/drivers/iommu/intel-iommu.c @@ -1827,17 +1827,10 @@ static int __domain_mapping(struct dmar_domain *domain, unsigned long iov_pfn, if (!pte) return -ENOMEM; /* It is large page*/ - if (largepage_lvl > 1) { + if (largepage_lvl > 1) pteval |= DMA_PTE_LARGE_PAGE; - /* Ensure that old small page tables are removed to make room - for superpage, if they exist. */ - dma_pte_clear_range(domain, iov_pfn, - iov_pfn + lvl_to_nr_pages(largepage_lvl) - 1); - dma_pte_free_pagetable(domain, iov_pfn, - iov_pfn + lvl_to_nr_pages(largepage_lvl) - 1); - } else { + else pteval &= ~(uint64_t)DMA_PTE_LARGE_PAGE; - } } /* We don't need lock here, nobody else @@ -2327,39 +2320,8 @@ static int domain_add_dev_info(struct dmar_domain *domain, return 0; } -static bool device_has_rmrr(struct pci_dev *dev) -{ - struct dmar_rmrr_unit *rmrr; - int i; - - for_each_rmrr_units(rmrr) { - for (i = 0; i < rmrr->devices_cnt; i++) { - /* - * Return TRUE if this RMRR contains the device that - * is passed in. - */ - if (rmrr->devices[i] == dev) - return true; - } - } - return false; -} - static int iommu_should_identity_map(struct pci_dev *pdev, int startup) { - - /* - * We want to prevent any device associated with an RMRR from - * getting placed into the SI Domain. This is done because - * problems exist when devices are moved in and out of domains - * and their respective RMRR info is lost. We exempt USB devices - * from this process due to their usage of RMRRs that are known - * to not be needed after BIOS hand-off to OS. - */ - if (device_has_rmrr(pdev) && - (pdev->class >> 8) != PCI_CLASS_SERIAL_USB) - return 0; - if ((iommu_identity_mapping & IDENTMAP_AZALIA) && IS_AZALIA(pdev)) return 1; diff --git a/trunk/drivers/iommu/omap-iommu.c b/trunk/drivers/iommu/omap-iommu.c index 18108c1405e2..badc17c2bcb4 100644 --- a/trunk/drivers/iommu/omap-iommu.c +++ b/trunk/drivers/iommu/omap-iommu.c @@ -16,13 +16,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include @@ -143,44 +143,31 @@ EXPORT_SYMBOL_GPL(omap_iommu_arch_version); static int iommu_enable(struct omap_iommu *obj) { int err; - struct platform_device *pdev = to_platform_device(obj->dev); - struct iommu_platform_data *pdata = pdev->dev.platform_data; - if (!obj || !pdata) + if (!obj) return -EINVAL; if (!arch_iommu) return -ENODEV; - if (pdata->deassert_reset) { - err = pdata->deassert_reset(pdev, pdata->reset_name); - if (err) { - dev_err(obj->dev, "deassert_reset failed: %d\n", err); - return err; - } - } - - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); err = arch_iommu->enable(obj); + clk_disable(obj->clk); return err; } static void iommu_disable(struct omap_iommu *obj) { - struct platform_device *pdev = to_platform_device(obj->dev); - struct iommu_platform_data *pdata = pdev->dev.platform_data; - - if (!obj || !pdata) + if (!obj) return; - arch_iommu->disable(obj); + clk_enable(obj->clk); - pm_runtime_put_sync(obj->dev); + arch_iommu->disable(obj); - if (pdata->assert_reset) - pdata->assert_reset(pdev, pdata->reset_name); + clk_disable(obj->clk); } /* @@ -303,7 +290,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) if (!obj || !obj->nr_tlb_entries || !e) return -EINVAL; - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); iotlb_lock_get(obj, &l); if (l.base == obj->nr_tlb_entries) { @@ -333,7 +320,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) cr = iotlb_alloc_cr(obj, e); if (IS_ERR(cr)) { - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); return PTR_ERR(cr); } @@ -347,7 +334,7 @@ static int load_iotlb_entry(struct omap_iommu *obj, struct iotlb_entry *e) l.vict = l.base; iotlb_lock_set(obj, &l); out: - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); return err; } @@ -377,7 +364,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da) int i; struct cr_regs cr; - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); for_each_iotlb_cr(obj, obj->nr_tlb_entries, i, cr) { u32 start; @@ -396,7 +383,7 @@ static void flush_iotlb_page(struct omap_iommu *obj, u32 da) iommu_write_reg(obj, 1, MMU_FLUSH_ENTRY); } } - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); if (i == obj->nr_tlb_entries) dev_dbg(obj->dev, "%s: no page for %08x\n", __func__, da); @@ -410,7 +397,7 @@ static void flush_iotlb_all(struct omap_iommu *obj) { struct iotlb_lock l; - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); l.base = 0; l.vict = 0; @@ -418,7 +405,7 @@ static void flush_iotlb_all(struct omap_iommu *obj) iommu_write_reg(obj, 1, MMU_GFLUSH); - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); } #if defined(CONFIG_OMAP_IOMMU_DEBUG) || defined(CONFIG_OMAP_IOMMU_DEBUG_MODULE) @@ -428,11 +415,11 @@ ssize_t omap_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t bytes) if (!obj || !buf) return -EINVAL; - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); bytes = arch_iommu->dump_ctx(obj, buf, bytes); - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); return bytes; } @@ -446,7 +433,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num) struct cr_regs tmp; struct cr_regs *p = crs; - pm_runtime_get_sync(obj->dev); + clk_enable(obj->clk); iotlb_lock_get(obj, &saved); for_each_iotlb_cr(obj, num, i, tmp) { @@ -456,7 +443,7 @@ __dump_tlb_entries(struct omap_iommu *obj, struct cr_regs *crs, int num) } iotlb_lock_set(obj, &saved); - pm_runtime_put_sync(obj->dev); + clk_disable(obj->clk); return p - crs; } @@ -820,7 +807,9 @@ static irqreturn_t iommu_fault_handler(int irq, void *data) if (!obj->refcount) return IRQ_NONE; + clk_enable(obj->clk); errs = iommu_report_fault(obj, &da); + clk_disable(obj->clk); if (errs == 0) return IRQ_HANDLED; @@ -942,10 +931,17 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) struct resource *res; struct iommu_platform_data *pdata = pdev->dev.platform_data; + if (pdev->num_resources != 2) + return -EINVAL; + obj = kzalloc(sizeof(*obj) + MMU_REG_SIZE, GFP_KERNEL); if (!obj) return -ENOMEM; + obj->clk = clk_get(&pdev->dev, pdata->clk_name); + if (IS_ERR(obj->clk)) + goto err_clk; + obj->nr_tlb_entries = pdata->nr_tlb_entries; obj->name = pdata->name; obj->dev = &pdev->dev; @@ -988,9 +984,6 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) goto err_irq; platform_set_drvdata(pdev, obj); - pm_runtime_irq_safe(obj->dev); - pm_runtime_enable(obj->dev); - dev_info(&pdev->dev, "%s registered\n", obj->name); return 0; @@ -999,6 +992,8 @@ static int __devinit omap_iommu_probe(struct platform_device *pdev) err_ioremap: release_mem_region(res->start, resource_size(res)); err_mem: + clk_put(obj->clk); +err_clk: kfree(obj); return err; } @@ -1019,8 +1014,7 @@ static int __devexit omap_iommu_remove(struct platform_device *pdev) release_mem_region(res->start, resource_size(res)); iounmap(obj->regbase); - pm_runtime_disable(obj->dev); - + clk_put(obj->clk); dev_info(&pdev->dev, "%s removed\n", obj->name); kfree(obj); return 0; diff --git a/trunk/drivers/iommu/omap-iommu.h b/trunk/drivers/iommu/omap-iommu.h index 120084206602..2b5f3c04d167 100644 --- a/trunk/drivers/iommu/omap-iommu.h +++ b/trunk/drivers/iommu/omap-iommu.h @@ -29,6 +29,7 @@ struct iotlb_entry { struct omap_iommu { const char *name; struct module *owner; + struct clk *clk; void __iomem *regbase; struct device *dev; void *isr_priv; @@ -115,6 +116,8 @@ static inline struct omap_iommu *dev_to_omap_iommu(struct device *dev) * MMU Register offsets */ #define MMU_REVISION 0x00 +#define MMU_SYSCONFIG 0x10 +#define MMU_SYSSTATUS 0x14 #define MMU_IRQSTATUS 0x18 #define MMU_IRQENABLE 0x1c #define MMU_WALKING_ST 0x40 diff --git a/trunk/drivers/iommu/omap-iommu2.c b/trunk/drivers/iommu/omap-iommu2.c index d745094a69dd..c02020292377 100644 --- a/trunk/drivers/iommu/omap-iommu2.c +++ b/trunk/drivers/iommu/omap-iommu2.c @@ -28,6 +28,19 @@ */ #define IOMMU_ARCH_VERSION 0x00000011 +/* SYSCONF */ +#define MMU_SYS_IDLE_SHIFT 3 +#define MMU_SYS_IDLE_FORCE (0 << MMU_SYS_IDLE_SHIFT) +#define MMU_SYS_IDLE_NONE (1 << MMU_SYS_IDLE_SHIFT) +#define MMU_SYS_IDLE_SMART (2 << MMU_SYS_IDLE_SHIFT) +#define MMU_SYS_IDLE_MASK (3 << MMU_SYS_IDLE_SHIFT) + +#define MMU_SYS_SOFTRESET (1 << 1) +#define MMU_SYS_AUTOIDLE 1 + +/* SYSSTATUS */ +#define MMU_SYS_RESETDONE 1 + /* IRQSTATUS & IRQENABLE */ #define MMU_IRQ_MULTIHITFAULT (1 << 4) #define MMU_IRQ_TABLEWALKFAULT (1 << 3) @@ -84,6 +97,7 @@ static void __iommu_set_twl(struct omap_iommu *obj, bool on) static int omap2_iommu_enable(struct omap_iommu *obj) { u32 l, pa; + unsigned long timeout; if (!obj->iopgd || !IS_ALIGNED((u32)obj->iopgd, SZ_16K)) return -EINVAL; @@ -92,10 +106,29 @@ static int omap2_iommu_enable(struct omap_iommu *obj) if (!IS_ALIGNED(pa, SZ_16K)) return -EINVAL; + iommu_write_reg(obj, MMU_SYS_SOFTRESET, MMU_SYSCONFIG); + + timeout = jiffies + msecs_to_jiffies(20); + do { + l = iommu_read_reg(obj, MMU_SYSSTATUS); + if (l & MMU_SYS_RESETDONE) + break; + } while (!time_after(jiffies, timeout)); + + if (!(l & MMU_SYS_RESETDONE)) { + dev_err(obj->dev, "can't take mmu out of reset\n"); + return -ENODEV; + } + l = iommu_read_reg(obj, MMU_REVISION); dev_info(obj->dev, "%s: version %d.%d\n", obj->name, (l >> 4) & 0xf, l & 0xf); + l = iommu_read_reg(obj, MMU_SYSCONFIG); + l &= ~MMU_SYS_IDLE_MASK; + l |= (MMU_SYS_IDLE_SMART | MMU_SYS_AUTOIDLE); + iommu_write_reg(obj, l, MMU_SYSCONFIG); + iommu_write_reg(obj, pa, MMU_TTB); __iommu_set_twl(obj, true); @@ -109,6 +142,7 @@ static void omap2_iommu_disable(struct omap_iommu *obj) l &= ~MMU_CNTL_MASK; iommu_write_reg(obj, l, MMU_CNTL); + iommu_write_reg(obj, MMU_SYS_IDLE_FORCE, MMU_SYSCONFIG); dev_dbg(obj->dev, "%s is shutting down\n", obj->name); } @@ -237,6 +271,8 @@ omap2_iommu_dump_ctx(struct omap_iommu *obj, char *buf, ssize_t len) char *p = buf; pr_reg(REVISION); + pr_reg(SYSCONFIG); + pr_reg(SYSSTATUS); pr_reg(IRQSTATUS); pr_reg(IRQENABLE); pr_reg(WALKING_ST); diff --git a/trunk/drivers/iommu/tegra-gart.c b/trunk/drivers/iommu/tegra-gart.c index 4c9db62814ff..c16e8fc8a4bd 100644 --- a/trunk/drivers/iommu/tegra-gart.c +++ b/trunk/drivers/iommu/tegra-gart.c @@ -398,7 +398,6 @@ static int tegra_gart_probe(struct platform_device *pdev) do_gart_setup(gart, NULL); gart_handle = gart; - bus_set_iommu(&platform_bus_type, &gart_iommu_ops); return 0; fail: @@ -451,6 +450,7 @@ static struct platform_driver tegra_gart_driver = { static int __devinit tegra_gart_init(void) { + bus_set_iommu(&platform_bus_type, &gart_iommu_ops); return platform_driver_register(&tegra_gart_driver); } diff --git a/trunk/drivers/iommu/tegra-smmu.c b/trunk/drivers/iommu/tegra-smmu.c index 25c1210c0832..4252d743963d 100644 --- a/trunk/drivers/iommu/tegra-smmu.c +++ b/trunk/drivers/iommu/tegra-smmu.c @@ -694,8 +694,10 @@ static void __smmu_iommu_unmap(struct smmu_as *as, dma_addr_t iova) *pte = _PTE_VACANT(iova); FLUSH_CPU_DCACHE(pte, page, sizeof(*pte)); flush_ptc_and_tlb(as->smmu, as, iova, pte, page, 0); - if (!--(*count)) + if (!--(*count)) { free_ptbl(as, iova); + smmu_flush_regs(as->smmu, 0); + } } static void __smmu_iommu_map_pfn(struct smmu_as *as, dma_addr_t iova, @@ -1230,7 +1232,6 @@ static int tegra_smmu_probe(struct platform_device *pdev) smmu_debugfs_create(smmu); smmu_handle = smmu; - bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return 0; } @@ -1275,6 +1276,7 @@ static struct platform_driver tegra_smmu_driver = { static int __devinit tegra_smmu_init(void) { + bus_set_iommu(&platform_bus_type, &smmu_iommu_ops); return platform_driver_register(&tegra_smmu_driver); } diff --git a/trunk/fs/Kconfig b/trunk/fs/Kconfig index cfe512fd1caf..eaff24a19502 100644 --- a/trunk/fs/Kconfig +++ b/trunk/fs/Kconfig @@ -220,7 +220,6 @@ source "fs/pstore/Kconfig" source "fs/sysv/Kconfig" source "fs/ufs/Kconfig" source "fs/exofs/Kconfig" -source "fs/f2fs/Kconfig" endif # MISC_FILESYSTEMS diff --git a/trunk/fs/Makefile b/trunk/fs/Makefile index 9d53192236fc..1d7af79288a0 100644 --- a/trunk/fs/Makefile +++ b/trunk/fs/Makefile @@ -123,7 +123,6 @@ obj-$(CONFIG_DEBUG_FS) += debugfs/ obj-$(CONFIG_OCFS2_FS) += ocfs2/ obj-$(CONFIG_BTRFS_FS) += btrfs/ obj-$(CONFIG_GFS2_FS) += gfs2/ -obj-$(CONFIG_F2FS_FS) += f2fs/ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ diff --git a/trunk/fs/btrfs/ctree.c b/trunk/fs/btrfs/ctree.c index eea5da7a2b9a..c7b67cf24bba 100644 --- a/trunk/fs/btrfs/ctree.c +++ b/trunk/fs/btrfs/ctree.c @@ -1138,13 +1138,13 @@ __tree_mod_log_rewind(struct extent_buffer *eb, u64 time_seq, switch (tm->op) { case MOD_LOG_KEY_REMOVE_WHILE_FREEING: BUG_ON(tm->slot < n); - case MOD_LOG_KEY_REMOVE_WHILE_MOVING: case MOD_LOG_KEY_REMOVE: + n++; + case MOD_LOG_KEY_REMOVE_WHILE_MOVING: btrfs_set_node_key(eb, &tm->key, tm->slot); btrfs_set_node_blockptr(eb, tm->slot, tm->blockptr); btrfs_set_node_ptr_generation(eb, tm->slot, tm->generation); - n++; break; case MOD_LOG_KEY_REPLACE: BUG_ON(tm->slot >= n); @@ -4611,6 +4611,12 @@ static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, u32 nritems; int ret; + if (level) { + ret = tree_mod_log_insert_key(root->fs_info, parent, slot, + MOD_LOG_KEY_REMOVE); + BUG_ON(ret < 0); + } + nritems = btrfs_header_nritems(parent); if (slot != nritems - 1) { if (level) @@ -4621,10 +4627,6 @@ static void del_ptr(struct btrfs_trans_handle *trans, struct btrfs_root *root, btrfs_node_key_ptr_offset(slot + 1), sizeof(struct btrfs_key_ptr) * (nritems - slot - 1)); - } else if (level) { - ret = tree_mod_log_insert_key(root->fs_info, parent, slot, - MOD_LOG_KEY_REMOVE); - BUG_ON(ret < 0); } nritems--; diff --git a/trunk/fs/ceph/addr.c b/trunk/fs/ceph/addr.c index 064d1a68d2c1..6690269f5dde 100644 --- a/trunk/fs/ceph/addr.c +++ b/trunk/fs/ceph/addr.c @@ -267,14 +267,6 @@ static void finish_read(struct ceph_osd_request *req, struct ceph_msg *msg) kfree(req->r_pages); } -static void ceph_unlock_page_vector(struct page **pages, int num_pages) -{ - int i; - - for (i = 0; i < num_pages; i++) - unlock_page(pages[i]); -} - /* * start an async read(ahead) operation. return nr_pages we submitted * a read for on success, or negative error code. @@ -355,7 +347,6 @@ static int start_read(struct inode *inode, struct list_head *page_list, int max) return nr_pages; out_pages: - ceph_unlock_page_vector(pages, nr_pages); ceph_release_page_vector(pages, nr_pages); out: ceph_osdc_put_request(req); @@ -1087,51 +1078,23 @@ static int ceph_write_begin(struct file *file, struct address_space *mapping, struct page **pagep, void **fsdata) { struct inode *inode = file->f_dentry->d_inode; - struct ceph_inode_info *ci = ceph_inode(inode); - struct ceph_file_info *fi = file->private_data; struct page *page; pgoff_t index = pos >> PAGE_CACHE_SHIFT; - int r, want, got = 0; - - if (fi->fmode & CEPH_FILE_MODE_LAZY) - want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO; - else - want = CEPH_CAP_FILE_BUFFER; - - dout("write_begin %p %llx.%llx %llu~%u getting caps. i_size %llu\n", - inode, ceph_vinop(inode), pos, len, inode->i_size); - r = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, pos+len); - if (r < 0) - return r; - dout("write_begin %p %llx.%llx %llu~%u got cap refs on %s\n", - inode, ceph_vinop(inode), pos, len, ceph_cap_string(got)); - if (!(got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO))) { - ceph_put_cap_refs(ci, got); - return -EAGAIN; - } + int r; do { /* get a page */ page = grab_cache_page_write_begin(mapping, index, 0); - if (!page) { - r = -ENOMEM; - break; - } + if (!page) + return -ENOMEM; + *pagep = page; dout("write_begin file %p inode %p page %p %d~%d\n", file, inode, page, (int)pos, (int)len); r = ceph_update_writeable_page(file, pos, len, page); - if (r) - page_cache_release(page); } while (r == -EAGAIN); - if (r) { - ceph_put_cap_refs(ci, got); - } else { - *pagep = page; - *(int *)fsdata = got; - } return r; } @@ -1145,12 +1108,10 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, struct page *page, void *fsdata) { struct inode *inode = file->f_dentry->d_inode; - struct ceph_inode_info *ci = ceph_inode(inode); struct ceph_fs_client *fsc = ceph_inode_to_client(inode); struct ceph_mds_client *mdsc = fsc->mdsc; unsigned from = pos & (PAGE_CACHE_SIZE - 1); int check_cap = 0; - int got = (unsigned long)fsdata; dout("write_end file %p inode %p page %p %d~%d (%d)\n", file, inode, page, (int)pos, (int)copied, (int)len); @@ -1173,19 +1134,6 @@ static int ceph_write_end(struct file *file, struct address_space *mapping, up_read(&mdsc->snap_rwsem); page_cache_release(page); - if (copied > 0) { - int dirty; - spin_lock(&ci->i_ceph_lock); - dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); - spin_unlock(&ci->i_ceph_lock); - if (dirty) - __mark_inode_dirty(inode, dirty); - } - - dout("write_end %p %llx.%llx %llu~%u dropping cap refs on %s\n", - inode, ceph_vinop(inode), pos, len, ceph_cap_string(got)); - ceph_put_cap_refs(ci, got); - if (check_cap) ceph_check_caps(ceph_inode(inode), CHECK_CAPS_AUTHONLY, NULL); diff --git a/trunk/fs/ceph/caps.c b/trunk/fs/ceph/caps.c index a1d9bb30c1bf..3251e9cc6401 100644 --- a/trunk/fs/ceph/caps.c +++ b/trunk/fs/ceph/caps.c @@ -236,10 +236,8 @@ static struct ceph_cap *get_cap(struct ceph_mds_client *mdsc, if (!ctx) { cap = kmem_cache_alloc(ceph_cap_cachep, GFP_NOFS); if (cap) { - spin_lock(&mdsc->caps_list_lock); mdsc->caps_use_count++; mdsc->caps_total_count++; - spin_unlock(&mdsc->caps_list_lock); } return cap; } @@ -1351,15 +1349,11 @@ int __ceph_mark_dirty_caps(struct ceph_inode_info *ci, int mask) if (!ci->i_head_snapc) ci->i_head_snapc = ceph_get_snap_context( ci->i_snap_realm->cached_context); - dout(" inode %p now dirty snapc %p auth cap %p\n", - &ci->vfs_inode, ci->i_head_snapc, ci->i_auth_cap); + dout(" inode %p now dirty snapc %p\n", &ci->vfs_inode, + ci->i_head_snapc); BUG_ON(!list_empty(&ci->i_dirty_item)); spin_lock(&mdsc->cap_dirty_lock); - if (ci->i_auth_cap) - list_add(&ci->i_dirty_item, &mdsc->cap_dirty); - else - list_add(&ci->i_dirty_item, - &mdsc->cap_dirty_migrating); + list_add(&ci->i_dirty_item, &mdsc->cap_dirty); spin_unlock(&mdsc->cap_dirty_lock); if (ci->i_flushing_caps == 0) { ihold(inode); @@ -2394,7 +2388,7 @@ static void handle_cap_grant(struct inode *inode, struct ceph_mds_caps *grant, &atime); /* max size increase? */ - if (ci->i_auth_cap == cap && max_size != ci->i_max_size) { + if (max_size != ci->i_max_size) { dout("max_size %lld -> %llu\n", ci->i_max_size, max_size); ci->i_max_size = max_size; if (max_size >= ci->i_wanted_max_size) { @@ -2751,7 +2745,6 @@ static void handle_cap_import(struct ceph_mds_client *mdsc, /* make sure we re-request max_size, if necessary */ spin_lock(&ci->i_ceph_lock); - ci->i_wanted_max_size = 0; /* reset */ ci->i_requested_max_size = 0; spin_unlock(&ci->i_ceph_lock); } @@ -2847,6 +2840,8 @@ void ceph_handle_caps(struct ceph_mds_session *session, case CEPH_CAP_OP_IMPORT: handle_cap_import(mdsc, inode, h, session, snaptrace, snaptrace_len); + ceph_check_caps(ceph_inode(inode), 0, session); + goto done_unlocked; } /* the rest require a cap */ @@ -2863,7 +2858,6 @@ void ceph_handle_caps(struct ceph_mds_session *session, switch (op) { case CEPH_CAP_OP_REVOKE: case CEPH_CAP_OP_GRANT: - case CEPH_CAP_OP_IMPORT: handle_cap_grant(inode, h, session, cap, msg->middle); goto done_unlocked; diff --git a/trunk/fs/ceph/file.c b/trunk/fs/ceph/file.c index e51558fca3a3..d4dfdcf76d7f 100644 --- a/trunk/fs/ceph/file.c +++ b/trunk/fs/ceph/file.c @@ -712,53 +712,63 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, struct ceph_osd_client *osdc = &ceph_sb_to_client(inode->i_sb)->client->osdc; loff_t endoff = pos + iov->iov_len; - int got = 0; - int ret, err, written; + int want, got = 0; + int ret, err; if (ceph_snap(inode) != CEPH_NOSNAP) return -EROFS; retry_snap: - written = 0; if (ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_FULL)) return -ENOSPC; __ceph_do_pending_vmtruncate(inode); + dout("aio_write %p %llx.%llx %llu~%u getting caps. i_size %llu\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + inode->i_size); + if (fi->fmode & CEPH_FILE_MODE_LAZY) + want = CEPH_CAP_FILE_BUFFER | CEPH_CAP_FILE_LAZYIO; + else + want = CEPH_CAP_FILE_BUFFER; + ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, want, &got, endoff); + if (ret < 0) + goto out_put; - /* - * try to do a buffered write. if we don't have sufficient - * caps, we'll get -EAGAIN from generic_file_aio_write, or a - * short write if we only get caps for some pages. - */ - if (!(iocb->ki_filp->f_flags & O_DIRECT) && - !(inode->i_sb->s_flags & MS_SYNCHRONOUS) && - !(fi->flags & CEPH_F_SYNC)) { - ret = generic_file_aio_write(iocb, iov, nr_segs, pos); - if (ret >= 0) - written = ret; + dout("aio_write %p %llx.%llx %llu~%u got cap refs on %s\n", + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + ceph_cap_string(got)); + + if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 || + (iocb->ki_filp->f_flags & O_DIRECT) || + (inode->i_sb->s_flags & MS_SYNCHRONOUS) || + (fi->flags & CEPH_F_SYNC)) { + ret = ceph_sync_write(file, iov->iov_base, iov->iov_len, + &iocb->ki_pos); + } else { + /* + * buffered write; drop Fw early to avoid slow + * revocation if we get stuck on balance_dirty_pages + */ + int dirty; + spin_lock(&ci->i_ceph_lock); + dirty = __ceph_mark_dirty_caps(ci, CEPH_CAP_FILE_WR); + spin_unlock(&ci->i_ceph_lock); + ceph_put_cap_refs(ci, got); + + ret = generic_file_aio_write(iocb, iov, nr_segs, pos); if ((ret >= 0 || ret == -EIOCBQUEUED) && ((file->f_flags & O_SYNC) || IS_SYNC(file->f_mapping->host) || ceph_osdmap_flag(osdc->osdmap, CEPH_OSDMAP_NEARFULL))) { - err = vfs_fsync_range(file, pos, pos + written - 1, 1); + err = vfs_fsync_range(file, pos, pos + ret - 1, 1); if (err < 0) ret = err; } - if ((ret < 0 && ret != -EAGAIN) || pos + written >= endoff) - goto out; - } - dout("aio_write %p %llx.%llx %llu~%u getting caps. i_size %llu\n", - inode, ceph_vinop(inode), pos + written, - (unsigned)iov->iov_len - written, inode->i_size); - ret = ceph_get_caps(ci, CEPH_CAP_FILE_WR, 0, &got, endoff); - if (ret < 0) + if (dirty) + __mark_inode_dirty(inode, dirty); goto out; + } - dout("aio_write %p %llx.%llx %llu~%u got cap refs on %s\n", - inode, ceph_vinop(inode), pos + written, - (unsigned)iov->iov_len - written, ceph_cap_string(got)); - ret = ceph_sync_write(file, iov->iov_base + written, - iov->iov_len - written, &iocb->ki_pos); if (ret >= 0) { int dirty; spin_lock(&ci->i_ceph_lock); @@ -767,10 +777,13 @@ static ssize_t ceph_aio_write(struct kiocb *iocb, const struct iovec *iov, if (dirty) __mark_inode_dirty(inode, dirty); } + +out_put: dout("aio_write %p %llx.%llx %llu~%u dropping cap refs on %s\n", - inode, ceph_vinop(inode), pos + written, - (unsigned)iov->iov_len - written, ceph_cap_string(got)); + inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len, + ceph_cap_string(got)); ceph_put_cap_refs(ci, got); + out: if (ret == -EOLDSNAPC) { dout("aio_write %p %llx.%llx %llu~%u got EOLDSNAPC, retrying\n", diff --git a/trunk/fs/ceph/inode.c b/trunk/fs/ceph/inode.c index 2971eaa65cdc..ba95eea201bf 100644 --- a/trunk/fs/ceph/inode.c +++ b/trunk/fs/ceph/inode.c @@ -1466,7 +1466,7 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) { struct ceph_inode_info *ci = ceph_inode(inode); u64 to; - int wrbuffer_refs, finish = 0; + int wrbuffer_refs, wake = 0; retry: spin_lock(&ci->i_ceph_lock); @@ -1498,18 +1498,15 @@ void __ceph_do_pending_vmtruncate(struct inode *inode) truncate_inode_pages(inode->i_mapping, to); spin_lock(&ci->i_ceph_lock); - if (to == ci->i_truncate_size) { - ci->i_truncate_pending = 0; - finish = 1; - } + ci->i_truncate_pending--; + if (ci->i_truncate_pending == 0) + wake = 1; spin_unlock(&ci->i_ceph_lock); - if (!finish) - goto retry; if (wrbuffer_refs == 0) ceph_check_caps(ci, CHECK_CAPS_AUTHONLY, NULL); - - wake_up_all(&ci->i_cap_wq); + if (wake) + wake_up_all(&ci->i_cap_wq); } diff --git a/trunk/fs/ceph/mds_client.c b/trunk/fs/ceph/mds_client.c index 9165eb8309eb..1bcf712655d9 100644 --- a/trunk/fs/ceph/mds_client.c +++ b/trunk/fs/ceph/mds_client.c @@ -1590,7 +1590,7 @@ static int set_request_path_attr(struct inode *rinode, struct dentry *rdentry, } else if (rpath || rino) { *ino = rino; *ppath = rpath; - *pathlen = rpath ? strlen(rpath) : 0; + *pathlen = strlen(rpath); dout(" path %.*s\n", *pathlen, rpath); } @@ -1876,14 +1876,9 @@ static int __do_request(struct ceph_mds_client *mdsc, static void __wake_requests(struct ceph_mds_client *mdsc, struct list_head *head) { - struct ceph_mds_request *req; - LIST_HEAD(tmp_list); - - list_splice_init(head, &tmp_list); + struct ceph_mds_request *req, *nreq; - while (!list_empty(&tmp_list)) { - req = list_entry(tmp_list.next, - struct ceph_mds_request, r_wait); + list_for_each_entry_safe(req, nreq, head, r_wait) { list_del_init(&req->r_wait); __do_request(mdsc, req); } diff --git a/trunk/fs/ceph/super.c b/trunk/fs/ceph/super.c index e86aa9948124..2eb43f211325 100644 --- a/trunk/fs/ceph/super.c +++ b/trunk/fs/ceph/super.c @@ -403,6 +403,8 @@ static int ceph_show_options(struct seq_file *m, struct dentry *root) seq_printf(m, ",mount_timeout=%d", opt->mount_timeout); if (opt->osd_idle_ttl != CEPH_OSD_IDLE_TTL_DEFAULT) seq_printf(m, ",osd_idle_ttl=%d", opt->osd_idle_ttl); + if (opt->osd_timeout != CEPH_OSD_TIMEOUT_DEFAULT) + seq_printf(m, ",osdtimeout=%d", opt->osd_timeout); if (opt->osd_keepalive_timeout != CEPH_OSD_KEEPALIVE_DEFAULT) seq_printf(m, ",osdkeepalivetimeout=%d", opt->osd_keepalive_timeout); @@ -847,7 +849,7 @@ static int ceph_register_bdi(struct super_block *sb, fsc->backing_dev_info.ra_pages = default_backing_dev_info.ra_pages; - err = bdi_register(&fsc->backing_dev_info, NULL, "ceph-%ld", + err = bdi_register(&fsc->backing_dev_info, NULL, "ceph-%d", atomic_long_inc_return(&bdi_seq)); if (!err) sb->s_bdi = &fsc->backing_dev_info; diff --git a/trunk/fs/exportfs/expfs.c b/trunk/fs/exportfs/expfs.c index 5df4bb4aab14..606bb074c501 100644 --- a/trunk/fs/exportfs/expfs.c +++ b/trunk/fs/exportfs/expfs.c @@ -322,10 +322,10 @@ static int export_encode_fh(struct inode *inode, struct fid *fid, if (parent && (len < 4)) { *max_len = 4; - return FILEID_INVALID; + return 255; } else if (len < 2) { *max_len = 2; - return FILEID_INVALID; + return 255; } len = 2; diff --git a/trunk/fs/f2fs/Kconfig b/trunk/fs/f2fs/Kconfig deleted file mode 100644 index fd27e7e6326e..000000000000 --- a/trunk/fs/f2fs/Kconfig +++ /dev/null @@ -1,53 +0,0 @@ -config F2FS_FS - tristate "F2FS filesystem support (EXPERIMENTAL)" - depends on BLOCK - help - F2FS is based on Log-structured File System (LFS), which supports - versatile "flash-friendly" features. The design has been focused on - addressing the fundamental issues in LFS, which are snowball effect - of wandering tree and high cleaning overhead. - - Since flash-based storages show different characteristics according to - the internal geometry or flash memory management schemes aka FTL, F2FS - and tools support various parameters not only for configuring on-disk - layout, but also for selecting allocation and cleaning algorithms. - - If unsure, say N. - -config F2FS_STAT_FS - bool "F2FS Status Information" - depends on F2FS_FS && DEBUG_FS - default y - help - /sys/kernel/debug/f2fs/ contains information about all the partitions - mounted as f2fs. Each file shows the whole f2fs information. - - /sys/kernel/debug/f2fs/status includes: - - major file system information managed by f2fs currently - - average SIT information about whole segments - - current memory footprint consumed by f2fs. - -config F2FS_FS_XATTR - bool "F2FS extended attributes" - depends on F2FS_FS - default y - help - Extended attributes are name:value pairs associated with inodes by - the kernel or by users (see the attr(5) manual page, or visit - for details). - - If unsure, say N. - -config F2FS_FS_POSIX_ACL - bool "F2FS Access Control Lists" - depends on F2FS_FS_XATTR - select FS_POSIX_ACL - default y - help - Posix Access Control Lists (ACLs) support permissions for users and - gourps beyond the owner/group/world scheme. - - To learn more about Access Control Lists, visit the POSIX ACLs for - Linux website . - - If you don't know what Access Control Lists are, say N diff --git a/trunk/fs/f2fs/Makefile b/trunk/fs/f2fs/Makefile deleted file mode 100644 index 27a0820340b9..000000000000 --- a/trunk/fs/f2fs/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -obj-$(CONFIG_F2FS_FS) += f2fs.o - -f2fs-y := dir.o file.o inode.o namei.o hash.o super.o -f2fs-y += checkpoint.o gc.o data.o node.o segment.o recovery.o -f2fs-$(CONFIG_F2FS_STAT_FS) += debug.o -f2fs-$(CONFIG_F2FS_FS_XATTR) += xattr.o -f2fs-$(CONFIG_F2FS_FS_POSIX_ACL) += acl.o diff --git a/trunk/fs/f2fs/acl.c b/trunk/fs/f2fs/acl.c deleted file mode 100644 index fed74d193ffb..000000000000 --- a/trunk/fs/f2fs/acl.c +++ /dev/null @@ -1,414 +0,0 @@ -/* - * fs/f2fs/acl.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Portions of this code from linux/fs/ext2/acl.c - * - * Copyright (C) 2001-2003 Andreas Gruenbacher, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include "f2fs.h" -#include "xattr.h" -#include "acl.h" - -#define get_inode_mode(i) ((is_inode_flag_set(F2FS_I(i), FI_ACL_MODE)) ? \ - (F2FS_I(i)->i_acl_mode) : ((i)->i_mode)) - -static inline size_t f2fs_acl_size(int count) -{ - if (count <= 4) { - return sizeof(struct f2fs_acl_header) + - count * sizeof(struct f2fs_acl_entry_short); - } else { - return sizeof(struct f2fs_acl_header) + - 4 * sizeof(struct f2fs_acl_entry_short) + - (count - 4) * sizeof(struct f2fs_acl_entry); - } -} - -static inline int f2fs_acl_count(size_t size) -{ - ssize_t s; - size -= sizeof(struct f2fs_acl_header); - s = size - 4 * sizeof(struct f2fs_acl_entry_short); - if (s < 0) { - if (size % sizeof(struct f2fs_acl_entry_short)) - return -1; - return size / sizeof(struct f2fs_acl_entry_short); - } else { - if (s % sizeof(struct f2fs_acl_entry)) - return -1; - return s / sizeof(struct f2fs_acl_entry) + 4; - } -} - -static struct posix_acl *f2fs_acl_from_disk(const char *value, size_t size) -{ - int i, count; - struct posix_acl *acl; - struct f2fs_acl_header *hdr = (struct f2fs_acl_header *)value; - struct f2fs_acl_entry *entry = (struct f2fs_acl_entry *)(hdr + 1); - const char *end = value + size; - - if (hdr->a_version != cpu_to_le32(F2FS_ACL_VERSION)) - return ERR_PTR(-EINVAL); - - count = f2fs_acl_count(size); - if (count < 0) - return ERR_PTR(-EINVAL); - if (count == 0) - return NULL; - - acl = posix_acl_alloc(count, GFP_KERNEL); - if (!acl) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < count; i++) { - - if ((char *)entry > end) - goto fail; - - acl->a_entries[i].e_tag = le16_to_cpu(entry->e_tag); - acl->a_entries[i].e_perm = le16_to_cpu(entry->e_perm); - - switch (acl->a_entries[i].e_tag) { - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - acl->a_entries[i].e_id = ACL_UNDEFINED_ID; - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry_short)); - break; - - case ACL_USER: - acl->a_entries[i].e_uid = - make_kuid(&init_user_ns, - le32_to_cpu(entry->e_id)); - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry)); - break; - case ACL_GROUP: - acl->a_entries[i].e_gid = - make_kgid(&init_user_ns, - le32_to_cpu(entry->e_id)); - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry)); - break; - default: - goto fail; - } - } - if ((char *)entry != end) - goto fail; - return acl; -fail: - posix_acl_release(acl); - return ERR_PTR(-EINVAL); -} - -static void *f2fs_acl_to_disk(const struct posix_acl *acl, size_t *size) -{ - struct f2fs_acl_header *f2fs_acl; - struct f2fs_acl_entry *entry; - int i; - - f2fs_acl = kmalloc(sizeof(struct f2fs_acl_header) + acl->a_count * - sizeof(struct f2fs_acl_entry), GFP_KERNEL); - if (!f2fs_acl) - return ERR_PTR(-ENOMEM); - - f2fs_acl->a_version = cpu_to_le32(F2FS_ACL_VERSION); - entry = (struct f2fs_acl_entry *)(f2fs_acl + 1); - - for (i = 0; i < acl->a_count; i++) { - - entry->e_tag = cpu_to_le16(acl->a_entries[i].e_tag); - entry->e_perm = cpu_to_le16(acl->a_entries[i].e_perm); - - switch (acl->a_entries[i].e_tag) { - case ACL_USER: - entry->e_id = cpu_to_le32( - from_kuid(&init_user_ns, - acl->a_entries[i].e_uid)); - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry)); - break; - case ACL_GROUP: - entry->e_id = cpu_to_le32( - from_kgid(&init_user_ns, - acl->a_entries[i].e_gid)); - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry)); - break; - case ACL_USER_OBJ: - case ACL_GROUP_OBJ: - case ACL_MASK: - case ACL_OTHER: - entry = (struct f2fs_acl_entry *)((char *)entry + - sizeof(struct f2fs_acl_entry_short)); - break; - default: - goto fail; - } - } - *size = f2fs_acl_size(acl->a_count); - return (void *)f2fs_acl; - -fail: - kfree(f2fs_acl); - return ERR_PTR(-EINVAL); -} - -struct posix_acl *f2fs_get_acl(struct inode *inode, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; - void *value = NULL; - struct posix_acl *acl; - int retval; - - if (!test_opt(sbi, POSIX_ACL)) - return NULL; - - acl = get_cached_acl(inode, type); - if (acl != ACL_NOT_CACHED) - return acl; - - if (type == ACL_TYPE_ACCESS) - name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; - - retval = f2fs_getxattr(inode, name_index, "", NULL, 0); - if (retval > 0) { - value = kmalloc(retval, GFP_KERNEL); - if (!value) - return ERR_PTR(-ENOMEM); - retval = f2fs_getxattr(inode, name_index, "", value, retval); - } - - if (retval < 0) { - if (retval == -ENODATA) - acl = NULL; - else - acl = ERR_PTR(retval); - } else { - acl = f2fs_acl_from_disk(value, retval); - } - kfree(value); - if (!IS_ERR(acl)) - set_cached_acl(inode, type, acl); - - return acl; -} - -static int f2fs_set_acl(struct inode *inode, int type, struct posix_acl *acl) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); - int name_index; - void *value = NULL; - size_t size = 0; - int error; - - if (!test_opt(sbi, POSIX_ACL)) - return 0; - if (S_ISLNK(inode->i_mode)) - return -EOPNOTSUPP; - - switch (type) { - case ACL_TYPE_ACCESS: - name_index = F2FS_XATTR_INDEX_POSIX_ACL_ACCESS; - if (acl) { - error = posix_acl_equiv_mode(acl, &inode->i_mode); - if (error < 0) - return error; - set_acl_inode(fi, inode->i_mode); - if (error == 0) - acl = NULL; - } - break; - - case ACL_TYPE_DEFAULT: - name_index = F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT; - if (!S_ISDIR(inode->i_mode)) - return acl ? -EACCES : 0; - break; - - default: - return -EINVAL; - } - - if (acl) { - value = f2fs_acl_to_disk(acl, &size); - if (IS_ERR(value)) { - cond_clear_inode_flag(fi, FI_ACL_MODE); - return (int)PTR_ERR(value); - } - } - - error = f2fs_setxattr(inode, name_index, "", value, size); - - kfree(value); - if (!error) - set_cached_acl(inode, type, acl); - - cond_clear_inode_flag(fi, FI_ACL_MODE); - return error; -} - -int f2fs_init_acl(struct inode *inode, struct inode *dir) -{ - struct posix_acl *acl = NULL; - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - int error = 0; - - if (!S_ISLNK(inode->i_mode)) { - if (test_opt(sbi, POSIX_ACL)) { - acl = f2fs_get_acl(dir, ACL_TYPE_DEFAULT); - if (IS_ERR(acl)) - return PTR_ERR(acl); - } - if (!acl) - inode->i_mode &= ~current_umask(); - } - - if (test_opt(sbi, POSIX_ACL) && acl) { - - if (S_ISDIR(inode->i_mode)) { - error = f2fs_set_acl(inode, ACL_TYPE_DEFAULT, acl); - if (error) - goto cleanup; - } - error = posix_acl_create(&acl, GFP_KERNEL, &inode->i_mode); - if (error < 0) - return error; - if (error > 0) - error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); - } -cleanup: - posix_acl_release(acl); - return error; -} - -int f2fs_acl_chmod(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct posix_acl *acl; - int error; - mode_t mode = get_inode_mode(inode); - - if (!test_opt(sbi, POSIX_ACL)) - return 0; - if (S_ISLNK(mode)) - return -EOPNOTSUPP; - - acl = f2fs_get_acl(inode, ACL_TYPE_ACCESS); - if (IS_ERR(acl) || !acl) - return PTR_ERR(acl); - - error = posix_acl_chmod(&acl, GFP_KERNEL, mode); - if (error) - return error; - error = f2fs_set_acl(inode, ACL_TYPE_ACCESS, acl); - posix_acl_release(acl); - return error; -} - -static size_t f2fs_xattr_list_acl(struct dentry *dentry, char *list, - size_t list_size, const char *name, size_t name_len, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - const char *xname = POSIX_ACL_XATTR_DEFAULT; - size_t size; - - if (!test_opt(sbi, POSIX_ACL)) - return 0; - - if (type == ACL_TYPE_ACCESS) - xname = POSIX_ACL_XATTR_ACCESS; - - size = strlen(xname) + 1; - if (list && size <= list_size) - memcpy(list, xname, size); - return size; -} - -static int f2fs_xattr_get_acl(struct dentry *dentry, const char *name, - void *buffer, size_t size, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - struct posix_acl *acl; - int error; - - if (strcmp(name, "") != 0) - return -EINVAL; - if (!test_opt(sbi, POSIX_ACL)) - return -EOPNOTSUPP; - - acl = f2fs_get_acl(dentry->d_inode, type); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (!acl) - return -ENODATA; - error = posix_acl_to_xattr(&init_user_ns, acl, buffer, size); - posix_acl_release(acl); - - return error; -} - -static int f2fs_xattr_set_acl(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - struct inode *inode = dentry->d_inode; - struct posix_acl *acl = NULL; - int error; - - if (strcmp(name, "") != 0) - return -EINVAL; - if (!test_opt(sbi, POSIX_ACL)) - return -EOPNOTSUPP; - if (!inode_owner_or_capable(inode)) - return -EPERM; - - if (value) { - acl = posix_acl_from_xattr(&init_user_ns, value, size); - if (IS_ERR(acl)) - return PTR_ERR(acl); - if (acl) { - error = posix_acl_valid(acl); - if (error) - goto release_and_out; - } - } else { - acl = NULL; - } - - error = f2fs_set_acl(inode, type, acl); - -release_and_out: - posix_acl_release(acl); - return error; -} - -const struct xattr_handler f2fs_xattr_acl_default_handler = { - .prefix = POSIX_ACL_XATTR_DEFAULT, - .flags = ACL_TYPE_DEFAULT, - .list = f2fs_xattr_list_acl, - .get = f2fs_xattr_get_acl, - .set = f2fs_xattr_set_acl, -}; - -const struct xattr_handler f2fs_xattr_acl_access_handler = { - .prefix = POSIX_ACL_XATTR_ACCESS, - .flags = ACL_TYPE_ACCESS, - .list = f2fs_xattr_list_acl, - .get = f2fs_xattr_get_acl, - .set = f2fs_xattr_set_acl, -}; diff --git a/trunk/fs/f2fs/acl.h b/trunk/fs/f2fs/acl.h deleted file mode 100644 index 80f430674417..000000000000 --- a/trunk/fs/f2fs/acl.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * fs/f2fs/acl.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Portions of this code from linux/fs/ext2/acl.h - * - * Copyright (C) 2001-2003 Andreas Gruenbacher, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __F2FS_ACL_H__ -#define __F2FS_ACL_H__ - -#include - -#define F2FS_ACL_VERSION 0x0001 - -struct f2fs_acl_entry { - __le16 e_tag; - __le16 e_perm; - __le32 e_id; -}; - -struct f2fs_acl_entry_short { - __le16 e_tag; - __le16 e_perm; -}; - -struct f2fs_acl_header { - __le32 a_version; -}; - -#ifdef CONFIG_F2FS_FS_POSIX_ACL - -extern struct posix_acl *f2fs_get_acl(struct inode *inode, int type); -extern int f2fs_acl_chmod(struct inode *inode); -extern int f2fs_init_acl(struct inode *inode, struct inode *dir); -#else -#define f2fs_check_acl NULL -#define f2fs_get_acl NULL -#define f2fs_set_acl NULL - -static inline int f2fs_acl_chmod(struct inode *inode) -{ - return 0; -} - -static inline int f2fs_init_acl(struct inode *inode, struct inode *dir) -{ - return 0; -} -#endif -#endif /* __F2FS_ACL_H__ */ diff --git a/trunk/fs/f2fs/checkpoint.c b/trunk/fs/f2fs/checkpoint.c deleted file mode 100644 index 6ef36c37e2be..000000000000 --- a/trunk/fs/f2fs/checkpoint.c +++ /dev/null @@ -1,794 +0,0 @@ -/* - * fs/f2fs/checkpoint.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" - -static struct kmem_cache *orphan_entry_slab; -static struct kmem_cache *inode_entry_slab; - -/* - * We guarantee no failure on the returned page. - */ -struct page *grab_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) -{ - struct address_space *mapping = sbi->meta_inode->i_mapping; - struct page *page = NULL; -repeat: - page = grab_cache_page(mapping, index); - if (!page) { - cond_resched(); - goto repeat; - } - - /* We wait writeback only inside grab_meta_page() */ - wait_on_page_writeback(page); - SetPageUptodate(page); - return page; -} - -/* - * We guarantee no failure on the returned page. - */ -struct page *get_meta_page(struct f2fs_sb_info *sbi, pgoff_t index) -{ - struct address_space *mapping = sbi->meta_inode->i_mapping; - struct page *page; -repeat: - page = grab_cache_page(mapping, index); - if (!page) { - cond_resched(); - goto repeat; - } - if (f2fs_readpage(sbi, page, index, READ_SYNC)) { - f2fs_put_page(page, 1); - goto repeat; - } - mark_page_accessed(page); - - /* We do not allow returning an errorneous page */ - return page; -} - -static int f2fs_write_meta_page(struct page *page, - struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int err; - - wait_on_page_writeback(page); - - err = write_meta_page(sbi, page, wbc); - if (err) { - wbc->pages_skipped++; - set_page_dirty(page); - } - - dec_page_count(sbi, F2FS_DIRTY_META); - - /* In this case, we should not unlock this page */ - if (err != AOP_WRITEPAGE_ACTIVATE) - unlock_page(page); - return err; -} - -static int f2fs_write_meta_pages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); - struct block_device *bdev = sbi->sb->s_bdev; - long written; - - if (wbc->for_kupdate) - return 0; - - if (get_pages(sbi, F2FS_DIRTY_META) == 0) - return 0; - - /* if mounting is failed, skip writing node pages */ - mutex_lock(&sbi->cp_mutex); - written = sync_meta_pages(sbi, META, bio_get_nr_vecs(bdev)); - mutex_unlock(&sbi->cp_mutex); - wbc->nr_to_write -= written; - return 0; -} - -long sync_meta_pages(struct f2fs_sb_info *sbi, enum page_type type, - long nr_to_write) -{ - struct address_space *mapping = sbi->meta_inode->i_mapping; - pgoff_t index = 0, end = LONG_MAX; - struct pagevec pvec; - long nwritten = 0; - struct writeback_control wbc = { - .for_reclaim = 0, - }; - - pagevec_init(&pvec, 0); - - while (index <= end) { - int i, nr_pages; - nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, - PAGECACHE_TAG_DIRTY, - min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); - if (nr_pages == 0) - break; - - for (i = 0; i < nr_pages; i++) { - struct page *page = pvec.pages[i]; - lock_page(page); - BUG_ON(page->mapping != mapping); - BUG_ON(!PageDirty(page)); - clear_page_dirty_for_io(page); - f2fs_write_meta_page(page, &wbc); - if (nwritten++ >= nr_to_write) - break; - } - pagevec_release(&pvec); - cond_resched(); - } - - if (nwritten) - f2fs_submit_bio(sbi, type, nr_to_write == LONG_MAX); - - return nwritten; -} - -static int f2fs_set_meta_page_dirty(struct page *page) -{ - struct address_space *mapping = page->mapping; - struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); - - SetPageUptodate(page); - if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); - inc_page_count(sbi, F2FS_DIRTY_META); - F2FS_SET_SB_DIRT(sbi); - return 1; - } - return 0; -} - -const struct address_space_operations f2fs_meta_aops = { - .writepage = f2fs_write_meta_page, - .writepages = f2fs_write_meta_pages, - .set_page_dirty = f2fs_set_meta_page_dirty, -}; - -int check_orphan_space(struct f2fs_sb_info *sbi) -{ - unsigned int max_orphans; - int err = 0; - - /* - * considering 512 blocks in a segment 5 blocks are needed for cp - * and log segment summaries. Remaining blocks are used to keep - * orphan entries with the limitation one reserved segment - * for cp pack we can have max 1020*507 orphan entries - */ - max_orphans = (sbi->blocks_per_seg - 5) * F2FS_ORPHANS_PER_BLOCK; - mutex_lock(&sbi->orphan_inode_mutex); - if (sbi->n_orphans >= max_orphans) - err = -ENOSPC; - mutex_unlock(&sbi->orphan_inode_mutex); - return err; -} - -void add_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) -{ - struct list_head *head, *this; - struct orphan_inode_entry *new = NULL, *orphan = NULL; - - mutex_lock(&sbi->orphan_inode_mutex); - head = &sbi->orphan_inode_list; - list_for_each(this, head) { - orphan = list_entry(this, struct orphan_inode_entry, list); - if (orphan->ino == ino) - goto out; - if (orphan->ino > ino) - break; - orphan = NULL; - } -retry: - new = kmem_cache_alloc(orphan_entry_slab, GFP_ATOMIC); - if (!new) { - cond_resched(); - goto retry; - } - new->ino = ino; - INIT_LIST_HEAD(&new->list); - - /* add new_oentry into list which is sorted by inode number */ - if (orphan) { - struct orphan_inode_entry *prev; - - /* get previous entry */ - prev = list_entry(orphan->list.prev, typeof(*prev), list); - if (&prev->list != head) - /* insert new orphan inode entry */ - list_add(&new->list, &prev->list); - else - list_add(&new->list, head); - } else { - list_add_tail(&new->list, head); - } - sbi->n_orphans++; -out: - mutex_unlock(&sbi->orphan_inode_mutex); -} - -void remove_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) -{ - struct list_head *this, *next, *head; - struct orphan_inode_entry *orphan; - - mutex_lock(&sbi->orphan_inode_mutex); - head = &sbi->orphan_inode_list; - list_for_each_safe(this, next, head) { - orphan = list_entry(this, struct orphan_inode_entry, list); - if (orphan->ino == ino) { - list_del(&orphan->list); - kmem_cache_free(orphan_entry_slab, orphan); - sbi->n_orphans--; - break; - } - } - mutex_unlock(&sbi->orphan_inode_mutex); -} - -static void recover_orphan_inode(struct f2fs_sb_info *sbi, nid_t ino) -{ - struct inode *inode = f2fs_iget(sbi->sb, ino); - BUG_ON(IS_ERR(inode)); - clear_nlink(inode); - - /* truncate all the data during iput */ - iput(inode); -} - -int recover_orphan_inodes(struct f2fs_sb_info *sbi) -{ - block_t start_blk, orphan_blkaddr, i, j; - - if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG)) - return 0; - - sbi->por_doing = 1; - start_blk = __start_cp_addr(sbi) + 1; - orphan_blkaddr = __start_sum_addr(sbi) - 1; - - for (i = 0; i < orphan_blkaddr; i++) { - struct page *page = get_meta_page(sbi, start_blk + i); - struct f2fs_orphan_block *orphan_blk; - - orphan_blk = (struct f2fs_orphan_block *)page_address(page); - for (j = 0; j < le32_to_cpu(orphan_blk->entry_count); j++) { - nid_t ino = le32_to_cpu(orphan_blk->ino[j]); - recover_orphan_inode(sbi, ino); - } - f2fs_put_page(page, 1); - } - /* clear Orphan Flag */ - clear_ckpt_flags(F2FS_CKPT(sbi), CP_ORPHAN_PRESENT_FLAG); - sbi->por_doing = 0; - return 0; -} - -static void write_orphan_inodes(struct f2fs_sb_info *sbi, block_t start_blk) -{ - struct list_head *head, *this, *next; - struct f2fs_orphan_block *orphan_blk = NULL; - struct page *page = NULL; - unsigned int nentries = 0; - unsigned short index = 1; - unsigned short orphan_blocks; - - orphan_blocks = (unsigned short)((sbi->n_orphans + - (F2FS_ORPHANS_PER_BLOCK - 1)) / F2FS_ORPHANS_PER_BLOCK); - - mutex_lock(&sbi->orphan_inode_mutex); - head = &sbi->orphan_inode_list; - - /* loop for each orphan inode entry and write them in Jornal block */ - list_for_each_safe(this, next, head) { - struct orphan_inode_entry *orphan; - - orphan = list_entry(this, struct orphan_inode_entry, list); - - if (nentries == F2FS_ORPHANS_PER_BLOCK) { - /* - * an orphan block is full of 1020 entries, - * then we need to flush current orphan blocks - * and bring another one in memory - */ - orphan_blk->blk_addr = cpu_to_le16(index); - orphan_blk->blk_count = cpu_to_le16(orphan_blocks); - orphan_blk->entry_count = cpu_to_le32(nentries); - set_page_dirty(page); - f2fs_put_page(page, 1); - index++; - start_blk++; - nentries = 0; - page = NULL; - } - if (page) - goto page_exist; - - page = grab_meta_page(sbi, start_blk); - orphan_blk = (struct f2fs_orphan_block *)page_address(page); - memset(orphan_blk, 0, sizeof(*orphan_blk)); -page_exist: - orphan_blk->ino[nentries++] = cpu_to_le32(orphan->ino); - } - if (!page) - goto end; - - orphan_blk->blk_addr = cpu_to_le16(index); - orphan_blk->blk_count = cpu_to_le16(orphan_blocks); - orphan_blk->entry_count = cpu_to_le32(nentries); - set_page_dirty(page); - f2fs_put_page(page, 1); -end: - mutex_unlock(&sbi->orphan_inode_mutex); -} - -static struct page *validate_checkpoint(struct f2fs_sb_info *sbi, - block_t cp_addr, unsigned long long *version) -{ - struct page *cp_page_1, *cp_page_2 = NULL; - unsigned long blk_size = sbi->blocksize; - struct f2fs_checkpoint *cp_block; - unsigned long long cur_version = 0, pre_version = 0; - unsigned int crc = 0; - size_t crc_offset; - - /* Read the 1st cp block in this CP pack */ - cp_page_1 = get_meta_page(sbi, cp_addr); - - /* get the version number */ - cp_block = (struct f2fs_checkpoint *)page_address(cp_page_1); - crc_offset = le32_to_cpu(cp_block->checksum_offset); - if (crc_offset >= blk_size) - goto invalid_cp1; - - crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset); - if (!f2fs_crc_valid(crc, cp_block, crc_offset)) - goto invalid_cp1; - - pre_version = le64_to_cpu(cp_block->checkpoint_ver); - - /* Read the 2nd cp block in this CP pack */ - cp_addr += le32_to_cpu(cp_block->cp_pack_total_block_count) - 1; - cp_page_2 = get_meta_page(sbi, cp_addr); - - cp_block = (struct f2fs_checkpoint *)page_address(cp_page_2); - crc_offset = le32_to_cpu(cp_block->checksum_offset); - if (crc_offset >= blk_size) - goto invalid_cp2; - - crc = *(unsigned int *)((unsigned char *)cp_block + crc_offset); - if (!f2fs_crc_valid(crc, cp_block, crc_offset)) - goto invalid_cp2; - - cur_version = le64_to_cpu(cp_block->checkpoint_ver); - - if (cur_version == pre_version) { - *version = cur_version; - f2fs_put_page(cp_page_2, 1); - return cp_page_1; - } -invalid_cp2: - f2fs_put_page(cp_page_2, 1); -invalid_cp1: - f2fs_put_page(cp_page_1, 1); - return NULL; -} - -int get_valid_checkpoint(struct f2fs_sb_info *sbi) -{ - struct f2fs_checkpoint *cp_block; - struct f2fs_super_block *fsb = sbi->raw_super; - struct page *cp1, *cp2, *cur_page; - unsigned long blk_size = sbi->blocksize; - unsigned long long cp1_version = 0, cp2_version = 0; - unsigned long long cp_start_blk_no; - - sbi->ckpt = kzalloc(blk_size, GFP_KERNEL); - if (!sbi->ckpt) - return -ENOMEM; - /* - * Finding out valid cp block involves read both - * sets( cp pack1 and cp pack 2) - */ - cp_start_blk_no = le32_to_cpu(fsb->cp_blkaddr); - cp1 = validate_checkpoint(sbi, cp_start_blk_no, &cp1_version); - - /* The second checkpoint pack should start at the next segment */ - cp_start_blk_no += 1 << le32_to_cpu(fsb->log_blocks_per_seg); - cp2 = validate_checkpoint(sbi, cp_start_blk_no, &cp2_version); - - if (cp1 && cp2) { - if (ver_after(cp2_version, cp1_version)) - cur_page = cp2; - else - cur_page = cp1; - } else if (cp1) { - cur_page = cp1; - } else if (cp2) { - cur_page = cp2; - } else { - goto fail_no_cp; - } - - cp_block = (struct f2fs_checkpoint *)page_address(cur_page); - memcpy(sbi->ckpt, cp_block, blk_size); - - f2fs_put_page(cp1, 1); - f2fs_put_page(cp2, 1); - return 0; - -fail_no_cp: - kfree(sbi->ckpt); - return -EINVAL; -} - -void set_dirty_dir_page(struct inode *inode, struct page *page) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct list_head *head = &sbi->dir_inode_list; - struct dir_inode_entry *new; - struct list_head *this; - - if (!S_ISDIR(inode->i_mode)) - return; -retry: - new = kmem_cache_alloc(inode_entry_slab, GFP_NOFS); - if (!new) { - cond_resched(); - goto retry; - } - new->inode = inode; - INIT_LIST_HEAD(&new->list); - - spin_lock(&sbi->dir_inode_lock); - list_for_each(this, head) { - struct dir_inode_entry *entry; - entry = list_entry(this, struct dir_inode_entry, list); - if (entry->inode == inode) { - kmem_cache_free(inode_entry_slab, new); - goto out; - } - } - list_add_tail(&new->list, head); - sbi->n_dirty_dirs++; - - BUG_ON(!S_ISDIR(inode->i_mode)); -out: - inc_page_count(sbi, F2FS_DIRTY_DENTS); - inode_inc_dirty_dents(inode); - SetPagePrivate(page); - - spin_unlock(&sbi->dir_inode_lock); -} - -void remove_dirty_dir_inode(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct list_head *head = &sbi->dir_inode_list; - struct list_head *this; - - if (!S_ISDIR(inode->i_mode)) - return; - - spin_lock(&sbi->dir_inode_lock); - if (atomic_read(&F2FS_I(inode)->dirty_dents)) - goto out; - - list_for_each(this, head) { - struct dir_inode_entry *entry; - entry = list_entry(this, struct dir_inode_entry, list); - if (entry->inode == inode) { - list_del(&entry->list); - kmem_cache_free(inode_entry_slab, entry); - sbi->n_dirty_dirs--; - break; - } - } -out: - spin_unlock(&sbi->dir_inode_lock); -} - -void sync_dirty_dir_inodes(struct f2fs_sb_info *sbi) -{ - struct list_head *head = &sbi->dir_inode_list; - struct dir_inode_entry *entry; - struct inode *inode; -retry: - spin_lock(&sbi->dir_inode_lock); - if (list_empty(head)) { - spin_unlock(&sbi->dir_inode_lock); - return; - } - entry = list_entry(head->next, struct dir_inode_entry, list); - inode = igrab(entry->inode); - spin_unlock(&sbi->dir_inode_lock); - if (inode) { - filemap_flush(inode->i_mapping); - iput(inode); - } else { - /* - * We should submit bio, since it exists several - * wribacking dentry pages in the freeing inode. - */ - f2fs_submit_bio(sbi, DATA, true); - } - goto retry; -} - -/* - * Freeze all the FS-operations for checkpoint. - */ -void block_operations(struct f2fs_sb_info *sbi) -{ - int t; - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - - /* Stop renaming operation */ - mutex_lock_op(sbi, RENAME); - mutex_lock_op(sbi, DENTRY_OPS); - -retry_dents: - /* write all the dirty dentry pages */ - sync_dirty_dir_inodes(sbi); - - mutex_lock_op(sbi, DATA_WRITE); - if (get_pages(sbi, F2FS_DIRTY_DENTS)) { - mutex_unlock_op(sbi, DATA_WRITE); - goto retry_dents; - } - - /* block all the operations */ - for (t = DATA_NEW; t <= NODE_TRUNC; t++) - mutex_lock_op(sbi, t); - - mutex_lock(&sbi->write_inode); - - /* - * POR: we should ensure that there is no dirty node pages - * until finishing nat/sit flush. - */ -retry: - sync_node_pages(sbi, 0, &wbc); - - mutex_lock_op(sbi, NODE_WRITE); - - if (get_pages(sbi, F2FS_DIRTY_NODES)) { - mutex_unlock_op(sbi, NODE_WRITE); - goto retry; - } - mutex_unlock(&sbi->write_inode); -} - -static void unblock_operations(struct f2fs_sb_info *sbi) -{ - int t; - for (t = NODE_WRITE; t >= RENAME; t--) - mutex_unlock_op(sbi, t); -} - -static void do_checkpoint(struct f2fs_sb_info *sbi, bool is_umount) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - nid_t last_nid = 0; - block_t start_blk; - struct page *cp_page; - unsigned int data_sum_blocks, orphan_blocks; - unsigned int crc32 = 0; - void *kaddr; - int i; - - /* Flush all the NAT/SIT pages */ - while (get_pages(sbi, F2FS_DIRTY_META)) - sync_meta_pages(sbi, META, LONG_MAX); - - next_free_nid(sbi, &last_nid); - - /* - * modify checkpoint - * version number is already updated - */ - ckpt->elapsed_time = cpu_to_le64(get_mtime(sbi)); - ckpt->valid_block_count = cpu_to_le64(valid_user_blocks(sbi)); - ckpt->free_segment_count = cpu_to_le32(free_segments(sbi)); - for (i = 0; i < 3; i++) { - ckpt->cur_node_segno[i] = - cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_NODE)); - ckpt->cur_node_blkoff[i] = - cpu_to_le16(curseg_blkoff(sbi, i + CURSEG_HOT_NODE)); - ckpt->alloc_type[i + CURSEG_HOT_NODE] = - curseg_alloc_type(sbi, i + CURSEG_HOT_NODE); - } - for (i = 0; i < 3; i++) { - ckpt->cur_data_segno[i] = - cpu_to_le32(curseg_segno(sbi, i + CURSEG_HOT_DATA)); - ckpt->cur_data_blkoff[i] = - cpu_to_le16(curseg_blkoff(sbi, i + CURSEG_HOT_DATA)); - ckpt->alloc_type[i + CURSEG_HOT_DATA] = - curseg_alloc_type(sbi, i + CURSEG_HOT_DATA); - } - - ckpt->valid_node_count = cpu_to_le32(valid_node_count(sbi)); - ckpt->valid_inode_count = cpu_to_le32(valid_inode_count(sbi)); - ckpt->next_free_nid = cpu_to_le32(last_nid); - - /* 2 cp + n data seg summary + orphan inode blocks */ - data_sum_blocks = npages_for_summary_flush(sbi); - if (data_sum_blocks < 3) - set_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); - else - clear_ckpt_flags(ckpt, CP_COMPACT_SUM_FLAG); - - orphan_blocks = (sbi->n_orphans + F2FS_ORPHANS_PER_BLOCK - 1) - / F2FS_ORPHANS_PER_BLOCK; - ckpt->cp_pack_start_sum = cpu_to_le32(1 + orphan_blocks); - - if (is_umount) { - set_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - ckpt->cp_pack_total_block_count = cpu_to_le32(2 + - data_sum_blocks + orphan_blocks + NR_CURSEG_NODE_TYPE); - } else { - clear_ckpt_flags(ckpt, CP_UMOUNT_FLAG); - ckpt->cp_pack_total_block_count = cpu_to_le32(2 + - data_sum_blocks + orphan_blocks); - } - - if (sbi->n_orphans) - set_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); - else - clear_ckpt_flags(ckpt, CP_ORPHAN_PRESENT_FLAG); - - /* update SIT/NAT bitmap */ - get_sit_bitmap(sbi, __bitmap_ptr(sbi, SIT_BITMAP)); - get_nat_bitmap(sbi, __bitmap_ptr(sbi, NAT_BITMAP)); - - crc32 = f2fs_crc32(ckpt, le32_to_cpu(ckpt->checksum_offset)); - *(__le32 *)((unsigned char *)ckpt + - le32_to_cpu(ckpt->checksum_offset)) - = cpu_to_le32(crc32); - - start_blk = __start_cp_addr(sbi); - - /* write out checkpoint buffer at block 0 */ - cp_page = grab_meta_page(sbi, start_blk++); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, (1 << sbi->log_blocksize)); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - - if (sbi->n_orphans) { - write_orphan_inodes(sbi, start_blk); - start_blk += orphan_blocks; - } - - write_data_summaries(sbi, start_blk); - start_blk += data_sum_blocks; - if (is_umount) { - write_node_summaries(sbi, start_blk); - start_blk += NR_CURSEG_NODE_TYPE; - } - - /* writeout checkpoint block */ - cp_page = grab_meta_page(sbi, start_blk); - kaddr = page_address(cp_page); - memcpy(kaddr, ckpt, (1 << sbi->log_blocksize)); - set_page_dirty(cp_page); - f2fs_put_page(cp_page, 1); - - /* wait for previous submitted node/meta pages writeback */ - while (get_pages(sbi, F2FS_WRITEBACK)) - congestion_wait(BLK_RW_ASYNC, HZ / 50); - - filemap_fdatawait_range(sbi->node_inode->i_mapping, 0, LONG_MAX); - filemap_fdatawait_range(sbi->meta_inode->i_mapping, 0, LONG_MAX); - - /* update user_block_counts */ - sbi->last_valid_block_count = sbi->total_valid_block_count; - sbi->alloc_valid_block_count = 0; - - /* Here, we only have one bio having CP pack */ - if (is_set_ckpt_flags(ckpt, CP_ERROR_FLAG)) - sbi->sb->s_flags |= MS_RDONLY; - else - sync_meta_pages(sbi, META_FLUSH, LONG_MAX); - - clear_prefree_segments(sbi); - F2FS_RESET_SB_DIRT(sbi); -} - -/* - * We guarantee that this checkpoint procedure should not fail. - */ -void write_checkpoint(struct f2fs_sb_info *sbi, bool blocked, bool is_umount) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - unsigned long long ckpt_ver; - - if (!blocked) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - } - - f2fs_submit_bio(sbi, DATA, true); - f2fs_submit_bio(sbi, NODE, true); - f2fs_submit_bio(sbi, META, true); - - /* - * update checkpoint pack index - * Increase the version number so that - * SIT entries and seg summaries are written at correct place - */ - ckpt_ver = le64_to_cpu(ckpt->checkpoint_ver); - ckpt->checkpoint_ver = cpu_to_le64(++ckpt_ver); - - /* write cached NAT/SIT entries to NAT/SIT area */ - flush_nat_entries(sbi); - flush_sit_entries(sbi); - - reset_victim_segmap(sbi); - - /* unlock all the fs_lock[] in do_checkpoint() */ - do_checkpoint(sbi, is_umount); - - unblock_operations(sbi); - mutex_unlock(&sbi->cp_mutex); -} - -void init_orphan_info(struct f2fs_sb_info *sbi) -{ - mutex_init(&sbi->orphan_inode_mutex); - INIT_LIST_HEAD(&sbi->orphan_inode_list); - sbi->n_orphans = 0; -} - -int create_checkpoint_caches(void) -{ - orphan_entry_slab = f2fs_kmem_cache_create("f2fs_orphan_entry", - sizeof(struct orphan_inode_entry), NULL); - if (unlikely(!orphan_entry_slab)) - return -ENOMEM; - inode_entry_slab = f2fs_kmem_cache_create("f2fs_dirty_dir_entry", - sizeof(struct dir_inode_entry), NULL); - if (unlikely(!inode_entry_slab)) { - kmem_cache_destroy(orphan_entry_slab); - return -ENOMEM; - } - return 0; -} - -void destroy_checkpoint_caches(void) -{ - kmem_cache_destroy(orphan_entry_slab); - kmem_cache_destroy(inode_entry_slab); -} diff --git a/trunk/fs/f2fs/data.c b/trunk/fs/f2fs/data.c deleted file mode 100644 index 655aeabc1dd4..000000000000 --- a/trunk/fs/f2fs/data.c +++ /dev/null @@ -1,702 +0,0 @@ -/* - * fs/f2fs/data.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" - -/* - * Lock ordering for the change of data block address: - * ->data_page - * ->node_page - * update block addresses in the node page - */ -static void __set_data_blkaddr(struct dnode_of_data *dn, block_t new_addr) -{ - struct f2fs_node *rn; - __le32 *addr_array; - struct page *node_page = dn->node_page; - unsigned int ofs_in_node = dn->ofs_in_node; - - wait_on_page_writeback(node_page); - - rn = (struct f2fs_node *)page_address(node_page); - - /* Get physical address of data block */ - addr_array = blkaddr_in_node(rn); - addr_array[ofs_in_node] = cpu_to_le32(new_addr); - set_page_dirty(node_page); -} - -int reserve_new_block(struct dnode_of_data *dn) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - - if (is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC)) - return -EPERM; - if (!inc_valid_block_count(sbi, dn->inode, 1)) - return -ENOSPC; - - __set_data_blkaddr(dn, NEW_ADDR); - dn->data_blkaddr = NEW_ADDR; - sync_inode_page(dn); - return 0; -} - -static int check_extent_cache(struct inode *inode, pgoff_t pgofs, - struct buffer_head *bh_result) -{ - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - pgoff_t start_fofs, end_fofs; - block_t start_blkaddr; - - read_lock(&fi->ext.ext_lock); - if (fi->ext.len == 0) { - read_unlock(&fi->ext.ext_lock); - return 0; - } - - sbi->total_hit_ext++; - start_fofs = fi->ext.fofs; - end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; - - if (pgofs >= start_fofs && pgofs <= end_fofs) { - unsigned int blkbits = inode->i_sb->s_blocksize_bits; - size_t count; - - clear_buffer_new(bh_result); - map_bh(bh_result, inode->i_sb, - start_blkaddr + pgofs - start_fofs); - count = end_fofs - pgofs + 1; - if (count < (UINT_MAX >> blkbits)) - bh_result->b_size = (count << blkbits); - else - bh_result->b_size = UINT_MAX; - - sbi->read_hit_ext++; - read_unlock(&fi->ext.ext_lock); - return 1; - } - read_unlock(&fi->ext.ext_lock); - return 0; -} - -void update_extent_cache(block_t blk_addr, struct dnode_of_data *dn) -{ - struct f2fs_inode_info *fi = F2FS_I(dn->inode); - pgoff_t fofs, start_fofs, end_fofs; - block_t start_blkaddr, end_blkaddr; - - BUG_ON(blk_addr == NEW_ADDR); - fofs = start_bidx_of_node(ofs_of_node(dn->node_page)) + dn->ofs_in_node; - - /* Update the page address in the parent node */ - __set_data_blkaddr(dn, blk_addr); - - write_lock(&fi->ext.ext_lock); - - start_fofs = fi->ext.fofs; - end_fofs = fi->ext.fofs + fi->ext.len - 1; - start_blkaddr = fi->ext.blk_addr; - end_blkaddr = fi->ext.blk_addr + fi->ext.len - 1; - - /* Drop and initialize the matched extent */ - if (fi->ext.len == 1 && fofs == start_fofs) - fi->ext.len = 0; - - /* Initial extent */ - if (fi->ext.len == 0) { - if (blk_addr != NULL_ADDR) { - fi->ext.fofs = fofs; - fi->ext.blk_addr = blk_addr; - fi->ext.len = 1; - } - goto end_update; - } - - /* Frone merge */ - if (fofs == start_fofs - 1 && blk_addr == start_blkaddr - 1) { - fi->ext.fofs--; - fi->ext.blk_addr--; - fi->ext.len++; - goto end_update; - } - - /* Back merge */ - if (fofs == end_fofs + 1 && blk_addr == end_blkaddr + 1) { - fi->ext.len++; - goto end_update; - } - - /* Split the existing extent */ - if (fi->ext.len > 1 && - fofs >= start_fofs && fofs <= end_fofs) { - if ((end_fofs - fofs) < (fi->ext.len >> 1)) { - fi->ext.len = fofs - start_fofs; - } else { - fi->ext.fofs = fofs + 1; - fi->ext.blk_addr = start_blkaddr + - fofs - start_fofs + 1; - fi->ext.len -= fofs - start_fofs + 1; - } - goto end_update; - } - write_unlock(&fi->ext.ext_lock); - return; - -end_update: - write_unlock(&fi->ext.ext_lock); - sync_inode_page(dn); - return; -} - -struct page *find_data_page(struct inode *inode, pgoff_t index) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct address_space *mapping = inode->i_mapping; - struct dnode_of_data dn; - struct page *page; - int err; - - page = find_get_page(mapping, index); - if (page && PageUptodate(page)) - return page; - f2fs_put_page(page, 0); - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, RDONLY_NODE); - if (err) - return ERR_PTR(err); - f2fs_put_dnode(&dn); - - if (dn.data_blkaddr == NULL_ADDR) - return ERR_PTR(-ENOENT); - - /* By fallocate(), there is no cached page, but with NEW_ADDR */ - if (dn.data_blkaddr == NEW_ADDR) - return ERR_PTR(-EINVAL); - - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - unlock_page(page); - return page; -} - -/* - * If it tries to access a hole, return an error. - * Because, the callers, functions in dir.c and GC, should be able to know - * whether this page exists or not. - */ -struct page *get_lock_data_page(struct inode *inode, pgoff_t index) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct address_space *mapping = inode->i_mapping; - struct dnode_of_data dn; - struct page *page; - int err; - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, RDONLY_NODE); - if (err) - return ERR_PTR(err); - f2fs_put_dnode(&dn); - - if (dn.data_blkaddr == NULL_ADDR) - return ERR_PTR(-ENOENT); - - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - if (PageUptodate(page)) - return page; - - BUG_ON(dn.data_blkaddr == NEW_ADDR); - BUG_ON(dn.data_blkaddr == NULL_ADDR); - - err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - return page; -} - -/* - * Caller ensures that this data page is never allocated. - * A new zero-filled data page is allocated in the page cache. - */ -struct page *get_new_data_page(struct inode *inode, pgoff_t index, - bool new_i_size) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct address_space *mapping = inode->i_mapping; - struct page *page; - struct dnode_of_data dn; - int err; - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, 0); - if (err) - return ERR_PTR(err); - - if (dn.data_blkaddr == NULL_ADDR) { - if (reserve_new_block(&dn)) { - f2fs_put_dnode(&dn); - return ERR_PTR(-ENOSPC); - } - } - f2fs_put_dnode(&dn); - - page = grab_cache_page(mapping, index); - if (!page) - return ERR_PTR(-ENOMEM); - - if (PageUptodate(page)) - return page; - - if (dn.data_blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); - } else { - err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - } - SetPageUptodate(page); - - if (new_i_size && - i_size_read(inode) < ((index + 1) << PAGE_CACHE_SHIFT)) { - i_size_write(inode, ((index + 1) << PAGE_CACHE_SHIFT)); - mark_inode_dirty_sync(inode); - } - return page; -} - -static void read_end_io(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) { - SetPageUptodate(page); - } else { - ClearPageUptodate(page); - SetPageError(page); - } - unlock_page(page); - } while (bvec >= bio->bi_io_vec); - kfree(bio->bi_private); - bio_put(bio); -} - -/* - * Fill the locked page with data located in the block address. - * Read operation is synchronous, and caller must unlock the page. - */ -int f2fs_readpage(struct f2fs_sb_info *sbi, struct page *page, - block_t blk_addr, int type) -{ - struct block_device *bdev = sbi->sb->s_bdev; - bool sync = (type == READ_SYNC); - struct bio *bio; - - /* This page can be already read by other threads */ - if (PageUptodate(page)) { - if (!sync) - unlock_page(page); - return 0; - } - - down_read(&sbi->bio_sem); - - /* Allocate a new bio */ - bio = f2fs_bio_alloc(bdev, 1); - - /* Initialize the bio */ - bio->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); - bio->bi_end_io = read_end_io; - - if (bio_add_page(bio, page, PAGE_CACHE_SIZE, 0) < PAGE_CACHE_SIZE) { - kfree(bio->bi_private); - bio_put(bio); - up_read(&sbi->bio_sem); - return -EFAULT; - } - - submit_bio(type, bio); - up_read(&sbi->bio_sem); - - /* wait for read completion if sync */ - if (sync) { - lock_page(page); - if (PageError(page)) - return -EIO; - } - return 0; -} - -/* - * This function should be used by the data read flow only where it - * does not check the "create" flag that indicates block allocation. - * The reason for this special functionality is to exploit VFS readahead - * mechanism. - */ -static int get_data_block_ro(struct inode *inode, sector_t iblock, - struct buffer_head *bh_result, int create) -{ - unsigned int blkbits = inode->i_sb->s_blocksize_bits; - unsigned maxblocks = bh_result->b_size >> blkbits; - struct dnode_of_data dn; - pgoff_t pgofs; - int err; - - /* Get the page offset from the block offset(iblock) */ - pgofs = (pgoff_t)(iblock >> (PAGE_CACHE_SHIFT - blkbits)); - - if (check_extent_cache(inode, pgofs, bh_result)) - return 0; - - /* When reading holes, we need its node page */ - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, pgofs, RDONLY_NODE); - if (err) - return (err == -ENOENT) ? 0 : err; - - /* It does not support data allocation */ - BUG_ON(create); - - if (dn.data_blkaddr != NEW_ADDR && dn.data_blkaddr != NULL_ADDR) { - int i; - unsigned int end_offset; - - end_offset = IS_INODE(dn.node_page) ? - ADDRS_PER_INODE : - ADDRS_PER_BLOCK; - - clear_buffer_new(bh_result); - - /* Give more consecutive addresses for the read ahead */ - for (i = 0; i < end_offset - dn.ofs_in_node; i++) - if (((datablock_addr(dn.node_page, - dn.ofs_in_node + i)) - != (dn.data_blkaddr + i)) || maxblocks == i) - break; - map_bh(bh_result, inode->i_sb, dn.data_blkaddr); - bh_result->b_size = (i << blkbits); - } - f2fs_put_dnode(&dn); - return 0; -} - -static int f2fs_read_data_page(struct file *file, struct page *page) -{ - return mpage_readpage(page, get_data_block_ro); -} - -static int f2fs_read_data_pages(struct file *file, - struct address_space *mapping, - struct list_head *pages, unsigned nr_pages) -{ - return mpage_readpages(mapping, pages, nr_pages, get_data_block_ro); -} - -int do_write_data_page(struct page *page) -{ - struct inode *inode = page->mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - block_t old_blk_addr, new_blk_addr; - struct dnode_of_data dn; - int err = 0; - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, page->index, RDONLY_NODE); - if (err) - return err; - - old_blk_addr = dn.data_blkaddr; - - /* This page is already truncated */ - if (old_blk_addr == NULL_ADDR) - goto out_writepage; - - set_page_writeback(page); - - /* - * If current allocation needs SSR, - * it had better in-place writes for updated data. - */ - if (old_blk_addr != NEW_ADDR && !is_cold_data(page) && - need_inplace_update(inode)) { - rewrite_data_page(F2FS_SB(inode->i_sb), page, - old_blk_addr); - } else { - write_data_page(inode, page, &dn, - old_blk_addr, &new_blk_addr); - update_extent_cache(new_blk_addr, &dn); - F2FS_I(inode)->data_version = - le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver); - } -out_writepage: - f2fs_put_dnode(&dn); - return err; -} - -static int f2fs_write_data_page(struct page *page, - struct writeback_control *wbc) -{ - struct inode *inode = page->mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - loff_t i_size = i_size_read(inode); - const pgoff_t end_index = ((unsigned long long) i_size) - >> PAGE_CACHE_SHIFT; - unsigned offset; - int err = 0; - - if (page->index < end_index) - goto out; - - /* - * If the offset is out-of-range of file size, - * this page does not have to be written to disk. - */ - offset = i_size & (PAGE_CACHE_SIZE - 1); - if ((page->index >= end_index + 1) || !offset) { - if (S_ISDIR(inode->i_mode)) { - dec_page_count(sbi, F2FS_DIRTY_DENTS); - inode_dec_dirty_dents(inode); - } - goto unlock_out; - } - - zero_user_segment(page, offset, PAGE_CACHE_SIZE); -out: - if (sbi->por_doing) - goto redirty_out; - - if (wbc->for_reclaim && !S_ISDIR(inode->i_mode) && !is_cold_data(page)) - goto redirty_out; - - mutex_lock_op(sbi, DATA_WRITE); - if (S_ISDIR(inode->i_mode)) { - dec_page_count(sbi, F2FS_DIRTY_DENTS); - inode_dec_dirty_dents(inode); - } - err = do_write_data_page(page); - if (err && err != -ENOENT) { - wbc->pages_skipped++; - set_page_dirty(page); - } - mutex_unlock_op(sbi, DATA_WRITE); - - if (wbc->for_reclaim) - f2fs_submit_bio(sbi, DATA, true); - - if (err == -ENOENT) - goto unlock_out; - - clear_cold_data(page); - unlock_page(page); - - if (!wbc->for_reclaim && !S_ISDIR(inode->i_mode)) - f2fs_balance_fs(sbi); - return 0; - -unlock_out: - unlock_page(page); - return (err == -ENOENT) ? 0 : err; - -redirty_out: - wbc->pages_skipped++; - set_page_dirty(page); - return AOP_WRITEPAGE_ACTIVATE; -} - -#define MAX_DESIRED_PAGES_WP 4096 - -static int f2fs_write_data_pages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct inode *inode = mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int ret; - long excess_nrtw = 0, desired_nrtw; - - if (wbc->nr_to_write < MAX_DESIRED_PAGES_WP) { - desired_nrtw = MAX_DESIRED_PAGES_WP; - excess_nrtw = desired_nrtw - wbc->nr_to_write; - wbc->nr_to_write = desired_nrtw; - } - - if (!S_ISDIR(inode->i_mode)) - mutex_lock(&sbi->writepages); - ret = generic_writepages(mapping, wbc); - if (!S_ISDIR(inode->i_mode)) - mutex_unlock(&sbi->writepages); - f2fs_submit_bio(sbi, DATA, (wbc->sync_mode == WB_SYNC_ALL)); - - remove_dirty_dir_inode(inode); - - wbc->nr_to_write -= excess_nrtw; - return ret; -} - -static int f2fs_write_begin(struct file *file, struct address_space *mapping, - loff_t pos, unsigned len, unsigned flags, - struct page **pagep, void **fsdata) -{ - struct inode *inode = mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct page *page; - pgoff_t index = ((unsigned long long) pos) >> PAGE_CACHE_SHIFT; - struct dnode_of_data dn; - int err = 0; - - /* for nobh_write_end */ - *fsdata = NULL; - - f2fs_balance_fs(sbi); - - page = grab_cache_page_write_begin(mapping, index, flags); - if (!page) - return -ENOMEM; - *pagep = page; - - mutex_lock_op(sbi, DATA_NEW); - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, 0); - if (err) { - mutex_unlock_op(sbi, DATA_NEW); - f2fs_put_page(page, 1); - return err; - } - - if (dn.data_blkaddr == NULL_ADDR) { - err = reserve_new_block(&dn); - if (err) { - f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); - f2fs_put_page(page, 1); - return err; - } - } - f2fs_put_dnode(&dn); - - mutex_unlock_op(sbi, DATA_NEW); - - if ((len == PAGE_CACHE_SIZE) || PageUptodate(page)) - return 0; - - if ((pos & PAGE_CACHE_MASK) >= i_size_read(inode)) { - unsigned start = pos & (PAGE_CACHE_SIZE - 1); - unsigned end = start + len; - - /* Reading beyond i_size is simple: memset to zero */ - zero_user_segments(page, 0, start, end, PAGE_CACHE_SIZE); - return 0; - } - - if (dn.data_blkaddr == NEW_ADDR) { - zero_user_segment(page, 0, PAGE_CACHE_SIZE); - } else { - err = f2fs_readpage(sbi, page, dn.data_blkaddr, READ_SYNC); - if (err) { - f2fs_put_page(page, 1); - return err; - } - } - SetPageUptodate(page); - clear_cold_data(page); - return 0; -} - -static ssize_t f2fs_direct_IO(int rw, struct kiocb *iocb, - const struct iovec *iov, loff_t offset, unsigned long nr_segs) -{ - struct file *file = iocb->ki_filp; - struct inode *inode = file->f_mapping->host; - - if (rw == WRITE) - return 0; - - /* Needs synchronization with the cleaner */ - return blockdev_direct_IO(rw, iocb, inode, iov, offset, nr_segs, - get_data_block_ro); -} - -static void f2fs_invalidate_data_page(struct page *page, unsigned long offset) -{ - struct inode *inode = page->mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - if (S_ISDIR(inode->i_mode) && PageDirty(page)) { - dec_page_count(sbi, F2FS_DIRTY_DENTS); - inode_dec_dirty_dents(inode); - } - ClearPagePrivate(page); -} - -static int f2fs_release_data_page(struct page *page, gfp_t wait) -{ - ClearPagePrivate(page); - return 0; -} - -static int f2fs_set_data_page_dirty(struct page *page) -{ - struct address_space *mapping = page->mapping; - struct inode *inode = mapping->host; - - SetPageUptodate(page); - if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); - set_dirty_dir_page(inode, page); - return 1; - } - return 0; -} - -const struct address_space_operations f2fs_dblock_aops = { - .readpage = f2fs_read_data_page, - .readpages = f2fs_read_data_pages, - .writepage = f2fs_write_data_page, - .writepages = f2fs_write_data_pages, - .write_begin = f2fs_write_begin, - .write_end = nobh_write_end, - .set_page_dirty = f2fs_set_data_page_dirty, - .invalidatepage = f2fs_invalidate_data_page, - .releasepage = f2fs_release_data_page, - .direct_IO = f2fs_direct_IO, -}; diff --git a/trunk/fs/f2fs/debug.c b/trunk/fs/f2fs/debug.c deleted file mode 100644 index 0e0380a588ad..000000000000 --- a/trunk/fs/f2fs/debug.c +++ /dev/null @@ -1,361 +0,0 @@ -/* - * f2fs debugging statistics - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * Copyright (c) 2012 Linux Foundation - * Copyright (c) 2012 Greg Kroah-Hartman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" -#include "gc.h" - -static LIST_HEAD(f2fs_stat_list); -static struct dentry *debugfs_root; - -static void update_general_status(struct f2fs_sb_info *sbi) -{ - struct f2fs_stat_info *si = sbi->stat_info; - int i; - - /* valid check of the segment numbers */ - si->hit_ext = sbi->read_hit_ext; - si->total_ext = sbi->total_hit_ext; - si->ndirty_node = get_pages(sbi, F2FS_DIRTY_NODES); - si->ndirty_dent = get_pages(sbi, F2FS_DIRTY_DENTS); - si->ndirty_dirs = sbi->n_dirty_dirs; - si->ndirty_meta = get_pages(sbi, F2FS_DIRTY_META); - si->total_count = (int)sbi->user_block_count / sbi->blocks_per_seg; - si->rsvd_segs = reserved_segments(sbi); - si->overp_segs = overprovision_segments(sbi); - si->valid_count = valid_user_blocks(sbi); - si->valid_node_count = valid_node_count(sbi); - si->valid_inode_count = valid_inode_count(sbi); - si->utilization = utilization(sbi); - - si->free_segs = free_segments(sbi); - si->free_secs = free_sections(sbi); - si->prefree_count = prefree_segments(sbi); - si->dirty_count = dirty_segments(sbi); - si->node_pages = sbi->node_inode->i_mapping->nrpages; - si->meta_pages = sbi->meta_inode->i_mapping->nrpages; - si->nats = NM_I(sbi)->nat_cnt; - si->sits = SIT_I(sbi)->dirty_sentries; - si->fnids = NM_I(sbi)->fcnt; - si->bg_gc = sbi->bg_gc; - si->util_free = (int)(free_user_blocks(sbi) >> sbi->log_blocks_per_seg) - * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) - / 2; - si->util_valid = (int)(written_block_count(sbi) >> - sbi->log_blocks_per_seg) - * 100 / (int)(sbi->user_block_count >> sbi->log_blocks_per_seg) - / 2; - si->util_invalid = 50 - si->util_free - si->util_valid; - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_NODE; i++) { - struct curseg_info *curseg = CURSEG_I(sbi, i); - si->curseg[i] = curseg->segno; - si->cursec[i] = curseg->segno / sbi->segs_per_sec; - si->curzone[i] = si->cursec[i] / sbi->secs_per_zone; - } - - for (i = 0; i < 2; i++) { - si->segment_count[i] = sbi->segment_count[i]; - si->block_count[i] = sbi->block_count[i]; - } -} - -/* - * This function calculates BDF of every segments - */ -static void update_sit_info(struct f2fs_sb_info *sbi) -{ - struct f2fs_stat_info *si = sbi->stat_info; - unsigned int blks_per_sec, hblks_per_sec, total_vblocks, bimodal, dist; - struct sit_info *sit_i = SIT_I(sbi); - unsigned int segno, vblocks; - int ndirty = 0; - - bimodal = 0; - total_vblocks = 0; - blks_per_sec = sbi->segs_per_sec * (1 << sbi->log_blocks_per_seg); - hblks_per_sec = blks_per_sec / 2; - mutex_lock(&sit_i->sentry_lock); - for (segno = 0; segno < TOTAL_SEGS(sbi); segno += sbi->segs_per_sec) { - vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); - dist = abs(vblocks - hblks_per_sec); - bimodal += dist * dist; - - if (vblocks > 0 && vblocks < blks_per_sec) { - total_vblocks += vblocks; - ndirty++; - } - } - mutex_unlock(&sit_i->sentry_lock); - dist = sbi->total_sections * hblks_per_sec * hblks_per_sec / 100; - si->bimodal = bimodal / dist; - if (si->dirty_count) - si->avg_vblocks = total_vblocks / ndirty; - else - si->avg_vblocks = 0; -} - -/* - * This function calculates memory footprint. - */ -static void update_mem_info(struct f2fs_sb_info *sbi) -{ - struct f2fs_stat_info *si = sbi->stat_info; - unsigned npages; - - if (si->base_mem) - goto get_cache; - - si->base_mem = sizeof(struct f2fs_sb_info) + sbi->sb->s_blocksize; - si->base_mem += 2 * sizeof(struct f2fs_inode_info); - si->base_mem += sizeof(*sbi->ckpt); - - /* build sm */ - si->base_mem += sizeof(struct f2fs_sm_info); - - /* build sit */ - si->base_mem += sizeof(struct sit_info); - si->base_mem += TOTAL_SEGS(sbi) * sizeof(struct seg_entry); - si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi)); - si->base_mem += 2 * SIT_VBLOCK_MAP_SIZE * TOTAL_SEGS(sbi); - if (sbi->segs_per_sec > 1) - si->base_mem += sbi->total_sections * - sizeof(struct sec_entry); - si->base_mem += __bitmap_size(sbi, SIT_BITMAP); - - /* build free segmap */ - si->base_mem += sizeof(struct free_segmap_info); - si->base_mem += f2fs_bitmap_size(TOTAL_SEGS(sbi)); - si->base_mem += f2fs_bitmap_size(sbi->total_sections); - - /* build curseg */ - si->base_mem += sizeof(struct curseg_info) * NR_CURSEG_TYPE; - si->base_mem += PAGE_CACHE_SIZE * NR_CURSEG_TYPE; - - /* build dirty segmap */ - si->base_mem += sizeof(struct dirty_seglist_info); - si->base_mem += NR_DIRTY_TYPE * f2fs_bitmap_size(TOTAL_SEGS(sbi)); - si->base_mem += 2 * f2fs_bitmap_size(TOTAL_SEGS(sbi)); - - /* buld nm */ - si->base_mem += sizeof(struct f2fs_nm_info); - si->base_mem += __bitmap_size(sbi, NAT_BITMAP); - - /* build gc */ - si->base_mem += sizeof(struct f2fs_gc_kthread); - -get_cache: - /* free nids */ - si->cache_mem = NM_I(sbi)->fcnt; - si->cache_mem += NM_I(sbi)->nat_cnt; - npages = sbi->node_inode->i_mapping->nrpages; - si->cache_mem += npages << PAGE_CACHE_SHIFT; - npages = sbi->meta_inode->i_mapping->nrpages; - si->cache_mem += npages << PAGE_CACHE_SHIFT; - si->cache_mem += sbi->n_orphans * sizeof(struct orphan_inode_entry); - si->cache_mem += sbi->n_dirty_dirs * sizeof(struct dir_inode_entry); -} - -static int stat_show(struct seq_file *s, void *v) -{ - struct f2fs_stat_info *si, *next; - int i = 0; - int j; - - list_for_each_entry_safe(si, next, &f2fs_stat_list, stat_list) { - - mutex_lock(&si->stat_lock); - if (!si->sbi) { - mutex_unlock(&si->stat_lock); - continue; - } - update_general_status(si->sbi); - - seq_printf(s, "\n=====[ partition info. #%d ]=====\n", i++); - seq_printf(s, "[SB: 1] [CP: 2] [NAT: %d] [SIT: %d] ", - si->nat_area_segs, si->sit_area_segs); - seq_printf(s, "[SSA: %d] [MAIN: %d", - si->ssa_area_segs, si->main_area_segs); - seq_printf(s, "(OverProv:%d Resv:%d)]\n\n", - si->overp_segs, si->rsvd_segs); - seq_printf(s, "Utilization: %d%% (%d valid blocks)\n", - si->utilization, si->valid_count); - seq_printf(s, " - Node: %u (Inode: %u, ", - si->valid_node_count, si->valid_inode_count); - seq_printf(s, "Other: %u)\n - Data: %u\n", - si->valid_node_count - si->valid_inode_count, - si->valid_count - si->valid_node_count); - seq_printf(s, "\nMain area: %d segs, %d secs %d zones\n", - si->main_area_segs, si->main_area_sections, - si->main_area_zones); - seq_printf(s, " - COLD data: %d, %d, %d\n", - si->curseg[CURSEG_COLD_DATA], - si->cursec[CURSEG_COLD_DATA], - si->curzone[CURSEG_COLD_DATA]); - seq_printf(s, " - WARM data: %d, %d, %d\n", - si->curseg[CURSEG_WARM_DATA], - si->cursec[CURSEG_WARM_DATA], - si->curzone[CURSEG_WARM_DATA]); - seq_printf(s, " - HOT data: %d, %d, %d\n", - si->curseg[CURSEG_HOT_DATA], - si->cursec[CURSEG_HOT_DATA], - si->curzone[CURSEG_HOT_DATA]); - seq_printf(s, " - Dir dnode: %d, %d, %d\n", - si->curseg[CURSEG_HOT_NODE], - si->cursec[CURSEG_HOT_NODE], - si->curzone[CURSEG_HOT_NODE]); - seq_printf(s, " - File dnode: %d, %d, %d\n", - si->curseg[CURSEG_WARM_NODE], - si->cursec[CURSEG_WARM_NODE], - si->curzone[CURSEG_WARM_NODE]); - seq_printf(s, " - Indir nodes: %d, %d, %d\n", - si->curseg[CURSEG_COLD_NODE], - si->cursec[CURSEG_COLD_NODE], - si->curzone[CURSEG_COLD_NODE]); - seq_printf(s, "\n - Valid: %d\n - Dirty: %d\n", - si->main_area_segs - si->dirty_count - - si->prefree_count - si->free_segs, - si->dirty_count); - seq_printf(s, " - Prefree: %d\n - Free: %d (%d)\n\n", - si->prefree_count, si->free_segs, si->free_secs); - seq_printf(s, "GC calls: %d (BG: %d)\n", - si->call_count, si->bg_gc); - seq_printf(s, " - data segments : %d\n", si->data_segs); - seq_printf(s, " - node segments : %d\n", si->node_segs); - seq_printf(s, "Try to move %d blocks\n", si->tot_blks); - seq_printf(s, " - data blocks : %d\n", si->data_blks); - seq_printf(s, " - node blocks : %d\n", si->node_blks); - seq_printf(s, "\nExtent Hit Ratio: %d / %d\n", - si->hit_ext, si->total_ext); - seq_printf(s, "\nBalancing F2FS Async:\n"); - seq_printf(s, " - nodes %4d in %4d\n", - si->ndirty_node, si->node_pages); - seq_printf(s, " - dents %4d in dirs:%4d\n", - si->ndirty_dent, si->ndirty_dirs); - seq_printf(s, " - meta %4d in %4d\n", - si->ndirty_meta, si->meta_pages); - seq_printf(s, " - NATs %5d > %lu\n", - si->nats, NM_WOUT_THRESHOLD); - seq_printf(s, " - SITs: %5d\n - free_nids: %5d\n", - si->sits, si->fnids); - seq_printf(s, "\nDistribution of User Blocks:"); - seq_printf(s, " [ valid | invalid | free ]\n"); - seq_printf(s, " ["); - - for (j = 0; j < si->util_valid; j++) - seq_printf(s, "-"); - seq_printf(s, "|"); - - for (j = 0; j < si->util_invalid; j++) - seq_printf(s, "-"); - seq_printf(s, "|"); - - for (j = 0; j < si->util_free; j++) - seq_printf(s, "-"); - seq_printf(s, "]\n\n"); - seq_printf(s, "SSR: %u blocks in %u segments\n", - si->block_count[SSR], si->segment_count[SSR]); - seq_printf(s, "LFS: %u blocks in %u segments\n", - si->block_count[LFS], si->segment_count[LFS]); - - /* segment usage info */ - update_sit_info(si->sbi); - seq_printf(s, "\nBDF: %u, avg. vblocks: %u\n", - si->bimodal, si->avg_vblocks); - - /* memory footprint */ - update_mem_info(si->sbi); - seq_printf(s, "\nMemory: %u KB = static: %u + cached: %u\n", - (si->base_mem + si->cache_mem) >> 10, - si->base_mem >> 10, si->cache_mem >> 10); - mutex_unlock(&si->stat_lock); - } - return 0; -} - -static int stat_open(struct inode *inode, struct file *file) -{ - return single_open(file, stat_show, inode->i_private); -} - -static const struct file_operations stat_fops = { - .open = stat_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; - -static int init_stats(struct f2fs_sb_info *sbi) -{ - struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); - struct f2fs_stat_info *si; - - sbi->stat_info = kzalloc(sizeof(struct f2fs_stat_info), GFP_KERNEL); - if (!sbi->stat_info) - return -ENOMEM; - - si = sbi->stat_info; - mutex_init(&si->stat_lock); - list_add_tail(&si->stat_list, &f2fs_stat_list); - - si->all_area_segs = le32_to_cpu(raw_super->segment_count); - si->sit_area_segs = le32_to_cpu(raw_super->segment_count_sit); - si->nat_area_segs = le32_to_cpu(raw_super->segment_count_nat); - si->ssa_area_segs = le32_to_cpu(raw_super->segment_count_ssa); - si->main_area_segs = le32_to_cpu(raw_super->segment_count_main); - si->main_area_sections = le32_to_cpu(raw_super->section_count); - si->main_area_zones = si->main_area_sections / - le32_to_cpu(raw_super->secs_per_zone); - si->sbi = sbi; - return 0; -} - -int f2fs_build_stats(struct f2fs_sb_info *sbi) -{ - int retval; - - retval = init_stats(sbi); - if (retval) - return retval; - - if (!debugfs_root) - debugfs_root = debugfs_create_dir("f2fs", NULL); - - debugfs_create_file("status", S_IRUGO, debugfs_root, NULL, &stat_fops); - return 0; -} - -void f2fs_destroy_stats(struct f2fs_sb_info *sbi) -{ - struct f2fs_stat_info *si = sbi->stat_info; - - list_del(&si->stat_list); - mutex_lock(&si->stat_lock); - si->sbi = NULL; - mutex_unlock(&si->stat_lock); - kfree(sbi->stat_info); -} - -void destroy_root_stats(void) -{ - debugfs_remove_recursive(debugfs_root); - debugfs_root = NULL; -} diff --git a/trunk/fs/f2fs/dir.c b/trunk/fs/f2fs/dir.c deleted file mode 100644 index b4e24f32b54e..000000000000 --- a/trunk/fs/f2fs/dir.c +++ /dev/null @@ -1,672 +0,0 @@ -/* - * fs/f2fs/dir.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include "f2fs.h" -#include "acl.h" - -static unsigned long dir_blocks(struct inode *inode) -{ - return ((unsigned long long) (i_size_read(inode) + PAGE_CACHE_SIZE - 1)) - >> PAGE_CACHE_SHIFT; -} - -static unsigned int dir_buckets(unsigned int level) -{ - if (level < MAX_DIR_HASH_DEPTH / 2) - return 1 << level; - else - return 1 << ((MAX_DIR_HASH_DEPTH / 2) - 1); -} - -static unsigned int bucket_blocks(unsigned int level) -{ - if (level < MAX_DIR_HASH_DEPTH / 2) - return 2; - else - return 4; -} - -static unsigned char f2fs_filetype_table[F2FS_FT_MAX] = { - [F2FS_FT_UNKNOWN] = DT_UNKNOWN, - [F2FS_FT_REG_FILE] = DT_REG, - [F2FS_FT_DIR] = DT_DIR, - [F2FS_FT_CHRDEV] = DT_CHR, - [F2FS_FT_BLKDEV] = DT_BLK, - [F2FS_FT_FIFO] = DT_FIFO, - [F2FS_FT_SOCK] = DT_SOCK, - [F2FS_FT_SYMLINK] = DT_LNK, -}; - -#define S_SHIFT 12 -static unsigned char f2fs_type_by_mode[S_IFMT >> S_SHIFT] = { - [S_IFREG >> S_SHIFT] = F2FS_FT_REG_FILE, - [S_IFDIR >> S_SHIFT] = F2FS_FT_DIR, - [S_IFCHR >> S_SHIFT] = F2FS_FT_CHRDEV, - [S_IFBLK >> S_SHIFT] = F2FS_FT_BLKDEV, - [S_IFIFO >> S_SHIFT] = F2FS_FT_FIFO, - [S_IFSOCK >> S_SHIFT] = F2FS_FT_SOCK, - [S_IFLNK >> S_SHIFT] = F2FS_FT_SYMLINK, -}; - -static void set_de_type(struct f2fs_dir_entry *de, struct inode *inode) -{ - mode_t mode = inode->i_mode; - de->file_type = f2fs_type_by_mode[(mode & S_IFMT) >> S_SHIFT]; -} - -static unsigned long dir_block_index(unsigned int level, unsigned int idx) -{ - unsigned long i; - unsigned long bidx = 0; - - for (i = 0; i < level; i++) - bidx += dir_buckets(i) * bucket_blocks(i); - bidx += idx * bucket_blocks(level); - return bidx; -} - -static bool early_match_name(const char *name, int namelen, - f2fs_hash_t namehash, struct f2fs_dir_entry *de) -{ - if (le16_to_cpu(de->name_len) != namelen) - return false; - - if (de->hash_code != namehash) - return false; - - return true; -} - -static struct f2fs_dir_entry *find_in_block(struct page *dentry_page, - const char *name, int namelen, int *max_slots, - f2fs_hash_t namehash, struct page **res_page) -{ - struct f2fs_dir_entry *de; - unsigned long bit_pos, end_pos, next_pos; - struct f2fs_dentry_block *dentry_blk = kmap(dentry_page); - int slots; - - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, 0); - while (bit_pos < NR_DENTRY_IN_BLOCK) { - de = &dentry_blk->dentry[bit_pos]; - slots = GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - - if (early_match_name(name, namelen, namehash, de)) { - if (!memcmp(dentry_blk->filename[bit_pos], - name, namelen)) { - *res_page = dentry_page; - goto found; - } - } - next_pos = bit_pos + slots; - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, next_pos); - if (bit_pos >= NR_DENTRY_IN_BLOCK) - end_pos = NR_DENTRY_IN_BLOCK; - else - end_pos = bit_pos; - if (*max_slots < end_pos - next_pos) - *max_slots = end_pos - next_pos; - } - - de = NULL; - kunmap(dentry_page); -found: - return de; -} - -static struct f2fs_dir_entry *find_in_level(struct inode *dir, - unsigned int level, const char *name, int namelen, - f2fs_hash_t namehash, struct page **res_page) -{ - int s = GET_DENTRY_SLOTS(namelen); - unsigned int nbucket, nblock; - unsigned int bidx, end_block; - struct page *dentry_page; - struct f2fs_dir_entry *de = NULL; - bool room = false; - int max_slots = 0; - - BUG_ON(level > MAX_DIR_HASH_DEPTH); - - nbucket = dir_buckets(level); - nblock = bucket_blocks(level); - - bidx = dir_block_index(level, le32_to_cpu(namehash) % nbucket); - end_block = bidx + nblock; - - for (; bidx < end_block; bidx++) { - /* no need to allocate new dentry pages to all the indices */ - dentry_page = find_data_page(dir, bidx); - if (IS_ERR(dentry_page)) { - room = true; - continue; - } - - de = find_in_block(dentry_page, name, namelen, - &max_slots, namehash, res_page); - if (de) - break; - - if (max_slots >= s) - room = true; - f2fs_put_page(dentry_page, 0); - } - - if (!de && room && F2FS_I(dir)->chash != namehash) { - F2FS_I(dir)->chash = namehash; - F2FS_I(dir)->clevel = level; - } - - return de; -} - -/* - * Find an entry in the specified directory with the wanted name. - * It returns the page where the entry was found (as a parameter - res_page), - * and the entry itself. Page is returned mapped and unlocked. - * Entry is guaranteed to be valid. - */ -struct f2fs_dir_entry *f2fs_find_entry(struct inode *dir, - struct qstr *child, struct page **res_page) -{ - const char *name = child->name; - int namelen = child->len; - unsigned long npages = dir_blocks(dir); - struct f2fs_dir_entry *de = NULL; - f2fs_hash_t name_hash; - unsigned int max_depth; - unsigned int level; - - if (npages == 0) - return NULL; - - *res_page = NULL; - - name_hash = f2fs_dentry_hash(name, namelen); - max_depth = F2FS_I(dir)->i_current_depth; - - for (level = 0; level < max_depth; level++) { - de = find_in_level(dir, level, name, - namelen, name_hash, res_page); - if (de) - break; - } - if (!de && F2FS_I(dir)->chash != name_hash) { - F2FS_I(dir)->chash = name_hash; - F2FS_I(dir)->clevel = level - 1; - } - return de; -} - -struct f2fs_dir_entry *f2fs_parent_dir(struct inode *dir, struct page **p) -{ - struct page *page = NULL; - struct f2fs_dir_entry *de = NULL; - struct f2fs_dentry_block *dentry_blk = NULL; - - page = get_lock_data_page(dir, 0); - if (IS_ERR(page)) - return NULL; - - dentry_blk = kmap(page); - de = &dentry_blk->dentry[1]; - *p = page; - unlock_page(page); - return de; -} - -ino_t f2fs_inode_by_name(struct inode *dir, struct qstr *qstr) -{ - ino_t res = 0; - struct f2fs_dir_entry *de; - struct page *page; - - de = f2fs_find_entry(dir, qstr, &page); - if (de) { - res = le32_to_cpu(de->ino); - kunmap(page); - f2fs_put_page(page, 0); - } - - return res; -} - -void f2fs_set_link(struct inode *dir, struct f2fs_dir_entry *de, - struct page *page, struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - - mutex_lock_op(sbi, DENTRY_OPS); - lock_page(page); - wait_on_page_writeback(page); - de->ino = cpu_to_le32(inode->i_ino); - set_de_type(de, inode); - kunmap(page); - set_page_dirty(page); - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - mark_inode_dirty(dir); - - /* update parent inode number before releasing dentry page */ - F2FS_I(inode)->i_pino = dir->i_ino; - - f2fs_put_page(page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); -} - -void init_dent_inode(struct dentry *dentry, struct page *ipage) -{ - struct f2fs_node *rn; - - if (IS_ERR(ipage)) - return; - - wait_on_page_writeback(ipage); - - /* copy dentry info. to this inode page */ - rn = (struct f2fs_node *)page_address(ipage); - rn->i.i_namelen = cpu_to_le32(dentry->d_name.len); - memcpy(rn->i.i_name, dentry->d_name.name, dentry->d_name.len); - set_page_dirty(ipage); -} - -static int init_inode_metadata(struct inode *inode, struct dentry *dentry) -{ - struct inode *dir = dentry->d_parent->d_inode; - - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { - int err; - err = new_inode_page(inode, dentry); - if (err) - return err; - - if (S_ISDIR(inode->i_mode)) { - err = f2fs_make_empty(inode, dir); - if (err) { - remove_inode_page(inode); - return err; - } - } - - err = f2fs_init_acl(inode, dir); - if (err) { - remove_inode_page(inode); - return err; - } - } else { - struct page *ipage; - ipage = get_node_page(F2FS_SB(dir->i_sb), inode->i_ino); - if (IS_ERR(ipage)) - return PTR_ERR(ipage); - init_dent_inode(dentry, ipage); - f2fs_put_page(ipage, 1); - } - if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) { - inc_nlink(inode); - f2fs_write_inode(inode, NULL); - } - return 0; -} - -static void update_parent_metadata(struct inode *dir, struct inode *inode, - unsigned int current_depth) -{ - bool need_dir_update = false; - - if (is_inode_flag_set(F2FS_I(inode), FI_NEW_INODE)) { - if (S_ISDIR(inode->i_mode)) { - inc_nlink(dir); - need_dir_update = true; - } - clear_inode_flag(F2FS_I(inode), FI_NEW_INODE); - } - dir->i_mtime = dir->i_ctime = CURRENT_TIME; - if (F2FS_I(dir)->i_current_depth != current_depth) { - F2FS_I(dir)->i_current_depth = current_depth; - need_dir_update = true; - } - - if (need_dir_update) - f2fs_write_inode(dir, NULL); - else - mark_inode_dirty(dir); - - if (is_inode_flag_set(F2FS_I(inode), FI_INC_LINK)) - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); -} - -static int room_for_filename(struct f2fs_dentry_block *dentry_blk, int slots) -{ - int bit_start = 0; - int zero_start, zero_end; -next: - zero_start = find_next_zero_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - bit_start); - if (zero_start >= NR_DENTRY_IN_BLOCK) - return NR_DENTRY_IN_BLOCK; - - zero_end = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - zero_start); - if (zero_end - zero_start >= slots) - return zero_start; - - bit_start = zero_end + 1; - - if (zero_end + 1 >= NR_DENTRY_IN_BLOCK) - return NR_DENTRY_IN_BLOCK; - goto next; -} - -int f2fs_add_link(struct dentry *dentry, struct inode *inode) -{ - unsigned int bit_pos; - unsigned int level; - unsigned int current_depth; - unsigned long bidx, block; - f2fs_hash_t dentry_hash; - struct f2fs_dir_entry *de; - unsigned int nbucket, nblock; - struct inode *dir = dentry->d_parent->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - const char *name = dentry->d_name.name; - int namelen = dentry->d_name.len; - struct page *dentry_page = NULL; - struct f2fs_dentry_block *dentry_blk = NULL; - int slots = GET_DENTRY_SLOTS(namelen); - int err = 0; - int i; - - dentry_hash = f2fs_dentry_hash(name, dentry->d_name.len); - level = 0; - current_depth = F2FS_I(dir)->i_current_depth; - if (F2FS_I(dir)->chash == dentry_hash) { - level = F2FS_I(dir)->clevel; - F2FS_I(dir)->chash = 0; - } - -start: - if (current_depth == MAX_DIR_HASH_DEPTH) - return -ENOSPC; - - /* Increase the depth, if required */ - if (level == current_depth) - ++current_depth; - - nbucket = dir_buckets(level); - nblock = bucket_blocks(level); - - bidx = dir_block_index(level, (le32_to_cpu(dentry_hash) % nbucket)); - - for (block = bidx; block <= (bidx + nblock - 1); block++) { - mutex_lock_op(sbi, DENTRY_OPS); - dentry_page = get_new_data_page(dir, block, true); - if (IS_ERR(dentry_page)) { - mutex_unlock_op(sbi, DENTRY_OPS); - return PTR_ERR(dentry_page); - } - - dentry_blk = kmap(dentry_page); - bit_pos = room_for_filename(dentry_blk, slots); - if (bit_pos < NR_DENTRY_IN_BLOCK) - goto add_dentry; - - kunmap(dentry_page); - f2fs_put_page(dentry_page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); - } - - /* Move to next level to find the empty slot for new dentry */ - ++level; - goto start; -add_dentry: - err = init_inode_metadata(inode, dentry); - if (err) - goto fail; - - wait_on_page_writeback(dentry_page); - - de = &dentry_blk->dentry[bit_pos]; - de->hash_code = dentry_hash; - de->name_len = cpu_to_le16(namelen); - memcpy(dentry_blk->filename[bit_pos], name, namelen); - de->ino = cpu_to_le32(inode->i_ino); - set_de_type(de, inode); - for (i = 0; i < slots; i++) - test_and_set_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); - set_page_dirty(dentry_page); - - update_parent_metadata(dir, inode, current_depth); - - /* update parent inode number before releasing dentry page */ - F2FS_I(inode)->i_pino = dir->i_ino; -fail: - kunmap(dentry_page); - f2fs_put_page(dentry_page, 1); - mutex_unlock_op(sbi, DENTRY_OPS); - return err; -} - -/* - * It only removes the dentry from the dentry page,corresponding name - * entry in name page does not need to be touched during deletion. - */ -void f2fs_delete_entry(struct f2fs_dir_entry *dentry, struct page *page, - struct inode *inode) -{ - struct f2fs_dentry_block *dentry_blk; - unsigned int bit_pos; - struct address_space *mapping = page->mapping; - struct inode *dir = mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - int slots = GET_DENTRY_SLOTS(le16_to_cpu(dentry->name_len)); - void *kaddr = page_address(page); - int i; - - mutex_lock_op(sbi, DENTRY_OPS); - - lock_page(page); - wait_on_page_writeback(page); - - dentry_blk = (struct f2fs_dentry_block *)kaddr; - bit_pos = dentry - (struct f2fs_dir_entry *)dentry_blk->dentry; - for (i = 0; i < slots; i++) - test_and_clear_bit_le(bit_pos + i, &dentry_blk->dentry_bitmap); - - /* Let's check and deallocate this dentry page */ - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - 0); - kunmap(page); /* kunmap - pair of f2fs_find_entry */ - set_page_dirty(page); - - dir->i_ctime = dir->i_mtime = CURRENT_TIME; - - if (inode && S_ISDIR(inode->i_mode)) { - drop_nlink(dir); - f2fs_write_inode(dir, NULL); - } else { - mark_inode_dirty(dir); - } - - if (inode) { - inode->i_ctime = dir->i_ctime = dir->i_mtime = CURRENT_TIME; - drop_nlink(inode); - if (S_ISDIR(inode->i_mode)) { - drop_nlink(inode); - i_size_write(inode, 0); - } - f2fs_write_inode(inode, NULL); - if (inode->i_nlink == 0) - add_orphan_inode(sbi, inode->i_ino); - } - - if (bit_pos == NR_DENTRY_IN_BLOCK) { - truncate_hole(dir, page->index, page->index + 1); - clear_page_dirty_for_io(page); - ClearPageUptodate(page); - dec_page_count(sbi, F2FS_DIRTY_DENTS); - inode_dec_dirty_dents(dir); - } - f2fs_put_page(page, 1); - - mutex_unlock_op(sbi, DENTRY_OPS); -} - -int f2fs_make_empty(struct inode *inode, struct inode *parent) -{ - struct page *dentry_page; - struct f2fs_dentry_block *dentry_blk; - struct f2fs_dir_entry *de; - void *kaddr; - - dentry_page = get_new_data_page(inode, 0, true); - if (IS_ERR(dentry_page)) - return PTR_ERR(dentry_page); - - kaddr = kmap_atomic(dentry_page); - dentry_blk = (struct f2fs_dentry_block *)kaddr; - - de = &dentry_blk->dentry[0]; - de->name_len = cpu_to_le16(1); - de->hash_code = 0; - de->ino = cpu_to_le32(inode->i_ino); - memcpy(dentry_blk->filename[0], ".", 1); - set_de_type(de, inode); - - de = &dentry_blk->dentry[1]; - de->hash_code = 0; - de->name_len = cpu_to_le16(2); - de->ino = cpu_to_le32(parent->i_ino); - memcpy(dentry_blk->filename[1], "..", 2); - set_de_type(de, inode); - - test_and_set_bit_le(0, &dentry_blk->dentry_bitmap); - test_and_set_bit_le(1, &dentry_blk->dentry_bitmap); - kunmap_atomic(kaddr); - - set_page_dirty(dentry_page); - f2fs_put_page(dentry_page, 1); - return 0; -} - -bool f2fs_empty_dir(struct inode *dir) -{ - unsigned long bidx; - struct page *dentry_page; - unsigned int bit_pos; - struct f2fs_dentry_block *dentry_blk; - unsigned long nblock = dir_blocks(dir); - - for (bidx = 0; bidx < nblock; bidx++) { - void *kaddr; - dentry_page = get_lock_data_page(dir, bidx); - if (IS_ERR(dentry_page)) { - if (PTR_ERR(dentry_page) == -ENOENT) - continue; - else - return false; - } - - kaddr = kmap_atomic(dentry_page); - dentry_blk = (struct f2fs_dentry_block *)kaddr; - if (bidx == 0) - bit_pos = 2; - else - bit_pos = 0; - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - bit_pos); - kunmap_atomic(kaddr); - - f2fs_put_page(dentry_page, 1); - - if (bit_pos < NR_DENTRY_IN_BLOCK) - return false; - } - return true; -} - -static int f2fs_readdir(struct file *file, void *dirent, filldir_t filldir) -{ - unsigned long pos = file->f_pos; - struct inode *inode = file->f_dentry->d_inode; - unsigned long npages = dir_blocks(inode); - unsigned char *types = NULL; - unsigned int bit_pos = 0, start_bit_pos = 0; - int over = 0; - struct f2fs_dentry_block *dentry_blk = NULL; - struct f2fs_dir_entry *de = NULL; - struct page *dentry_page = NULL; - unsigned int n = 0; - unsigned char d_type = DT_UNKNOWN; - int slots; - - types = f2fs_filetype_table; - bit_pos = (pos % NR_DENTRY_IN_BLOCK); - n = (pos / NR_DENTRY_IN_BLOCK); - - for ( ; n < npages; n++) { - dentry_page = get_lock_data_page(inode, n); - if (IS_ERR(dentry_page)) - continue; - - start_bit_pos = bit_pos; - dentry_blk = kmap(dentry_page); - while (bit_pos < NR_DENTRY_IN_BLOCK) { - d_type = DT_UNKNOWN; - bit_pos = find_next_bit_le(&dentry_blk->dentry_bitmap, - NR_DENTRY_IN_BLOCK, - bit_pos); - if (bit_pos >= NR_DENTRY_IN_BLOCK) - break; - - de = &dentry_blk->dentry[bit_pos]; - if (types && de->file_type < F2FS_FT_MAX) - d_type = types[de->file_type]; - - over = filldir(dirent, - dentry_blk->filename[bit_pos], - le16_to_cpu(de->name_len), - (n * NR_DENTRY_IN_BLOCK) + bit_pos, - le32_to_cpu(de->ino), d_type); - if (over) { - file->f_pos += bit_pos - start_bit_pos; - goto success; - } - slots = GET_DENTRY_SLOTS(le16_to_cpu(de->name_len)); - bit_pos += slots; - } - bit_pos = 0; - file->f_pos = (n + 1) * NR_DENTRY_IN_BLOCK; - kunmap(dentry_page); - f2fs_put_page(dentry_page, 1); - dentry_page = NULL; - } -success: - if (dentry_page && !IS_ERR(dentry_page)) { - kunmap(dentry_page); - f2fs_put_page(dentry_page, 1); - } - - return 0; -} - -const struct file_operations f2fs_dir_operations = { - .llseek = generic_file_llseek, - .read = generic_read_dir, - .readdir = f2fs_readdir, - .fsync = f2fs_sync_file, - .unlocked_ioctl = f2fs_ioctl, -}; diff --git a/trunk/fs/f2fs/f2fs.h b/trunk/fs/f2fs/f2fs.h deleted file mode 100644 index a18d63db2fb6..000000000000 --- a/trunk/fs/f2fs/f2fs.h +++ /dev/null @@ -1,1083 +0,0 @@ -/* - * fs/f2fs/f2fs.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef _LINUX_F2FS_H -#define _LINUX_F2FS_H - -#include -#include -#include -#include -#include -#include - -/* - * For mount options - */ -#define F2FS_MOUNT_BG_GC 0x00000001 -#define F2FS_MOUNT_DISABLE_ROLL_FORWARD 0x00000002 -#define F2FS_MOUNT_DISCARD 0x00000004 -#define F2FS_MOUNT_NOHEAP 0x00000008 -#define F2FS_MOUNT_XATTR_USER 0x00000010 -#define F2FS_MOUNT_POSIX_ACL 0x00000020 -#define F2FS_MOUNT_DISABLE_EXT_IDENTIFY 0x00000040 - -#define clear_opt(sbi, option) (sbi->mount_opt.opt &= ~F2FS_MOUNT_##option) -#define set_opt(sbi, option) (sbi->mount_opt.opt |= F2FS_MOUNT_##option) -#define test_opt(sbi, option) (sbi->mount_opt.opt & F2FS_MOUNT_##option) - -#define ver_after(a, b) (typecheck(unsigned long long, a) && \ - typecheck(unsigned long long, b) && \ - ((long long)((a) - (b)) > 0)) - -typedef u64 block_t; -typedef u32 nid_t; - -struct f2fs_mount_info { - unsigned int opt; -}; - -static inline __u32 f2fs_crc32(void *buff, size_t len) -{ - return crc32_le(F2FS_SUPER_MAGIC, buff, len); -} - -static inline bool f2fs_crc_valid(__u32 blk_crc, void *buff, size_t buff_size) -{ - return f2fs_crc32(buff, buff_size) == blk_crc; -} - -/* - * For checkpoint manager - */ -enum { - NAT_BITMAP, - SIT_BITMAP -}; - -/* for the list of orphan inodes */ -struct orphan_inode_entry { - struct list_head list; /* list head */ - nid_t ino; /* inode number */ -}; - -/* for the list of directory inodes */ -struct dir_inode_entry { - struct list_head list; /* list head */ - struct inode *inode; /* vfs inode pointer */ -}; - -/* for the list of fsync inodes, used only during recovery */ -struct fsync_inode_entry { - struct list_head list; /* list head */ - struct inode *inode; /* vfs inode pointer */ - block_t blkaddr; /* block address locating the last inode */ -}; - -#define nats_in_cursum(sum) (le16_to_cpu(sum->n_nats)) -#define sits_in_cursum(sum) (le16_to_cpu(sum->n_sits)) - -#define nat_in_journal(sum, i) (sum->nat_j.entries[i].ne) -#define nid_in_journal(sum, i) (sum->nat_j.entries[i].nid) -#define sit_in_journal(sum, i) (sum->sit_j.entries[i].se) -#define segno_in_journal(sum, i) (sum->sit_j.entries[i].segno) - -static inline int update_nats_in_cursum(struct f2fs_summary_block *rs, int i) -{ - int before = nats_in_cursum(rs); - rs->n_nats = cpu_to_le16(before + i); - return before; -} - -static inline int update_sits_in_cursum(struct f2fs_summary_block *rs, int i) -{ - int before = sits_in_cursum(rs); - rs->n_sits = cpu_to_le16(before + i); - return before; -} - -/* - * For INODE and NODE manager - */ -#define XATTR_NODE_OFFSET (-1) /* - * store xattrs to one node block per - * file keeping -1 as its node offset to - * distinguish from index node blocks. - */ -#define RDONLY_NODE 1 /* - * specify a read-only mode when getting - * a node block. 0 is read-write mode. - * used by get_dnode_of_data(). - */ -#define F2FS_LINK_MAX 32000 /* maximum link count per file */ - -/* for in-memory extent cache entry */ -struct extent_info { - rwlock_t ext_lock; /* rwlock for consistency */ - unsigned int fofs; /* start offset in a file */ - u32 blk_addr; /* start block address of the extent */ - unsigned int len; /* lenth of the extent */ -}; - -/* - * i_advise uses FADVISE_XXX_BIT. We can add additional hints later. - */ -#define FADVISE_COLD_BIT 0x01 - -struct f2fs_inode_info { - struct inode vfs_inode; /* serve a vfs inode */ - unsigned long i_flags; /* keep an inode flags for ioctl */ - unsigned char i_advise; /* use to give file attribute hints */ - unsigned int i_current_depth; /* use only in directory structure */ - unsigned int i_pino; /* parent inode number */ - umode_t i_acl_mode; /* keep file acl mode temporarily */ - - /* Use below internally in f2fs*/ - unsigned long flags; /* use to pass per-file flags */ - unsigned long long data_version;/* lastes version of data for fsync */ - atomic_t dirty_dents; /* # of dirty dentry pages */ - f2fs_hash_t chash; /* hash value of given file name */ - unsigned int clevel; /* maximum level of given file name */ - nid_t i_xattr_nid; /* node id that contains xattrs */ - struct extent_info ext; /* in-memory extent cache entry */ -}; - -static inline void get_extent_info(struct extent_info *ext, - struct f2fs_extent i_ext) -{ - write_lock(&ext->ext_lock); - ext->fofs = le32_to_cpu(i_ext.fofs); - ext->blk_addr = le32_to_cpu(i_ext.blk_addr); - ext->len = le32_to_cpu(i_ext.len); - write_unlock(&ext->ext_lock); -} - -static inline void set_raw_extent(struct extent_info *ext, - struct f2fs_extent *i_ext) -{ - read_lock(&ext->ext_lock); - i_ext->fofs = cpu_to_le32(ext->fofs); - i_ext->blk_addr = cpu_to_le32(ext->blk_addr); - i_ext->len = cpu_to_le32(ext->len); - read_unlock(&ext->ext_lock); -} - -struct f2fs_nm_info { - block_t nat_blkaddr; /* base disk address of NAT */ - nid_t max_nid; /* maximum possible node ids */ - nid_t init_scan_nid; /* the first nid to be scanned */ - nid_t next_scan_nid; /* the next nid to be scanned */ - - /* NAT cache management */ - struct radix_tree_root nat_root;/* root of the nat entry cache */ - rwlock_t nat_tree_lock; /* protect nat_tree_lock */ - unsigned int nat_cnt; /* the # of cached nat entries */ - struct list_head nat_entries; /* cached nat entry list (clean) */ - struct list_head dirty_nat_entries; /* cached nat entry list (dirty) */ - - /* free node ids management */ - struct list_head free_nid_list; /* a list for free nids */ - spinlock_t free_nid_list_lock; /* protect free nid list */ - unsigned int fcnt; /* the number of free node id */ - struct mutex build_lock; /* lock for build free nids */ - - /* for checkpoint */ - char *nat_bitmap; /* NAT bitmap pointer */ - int bitmap_size; /* bitmap size */ -}; - -/* - * this structure is used as one of function parameters. - * all the information are dedicated to a given direct node block determined - * by the data offset in a file. - */ -struct dnode_of_data { - struct inode *inode; /* vfs inode pointer */ - struct page *inode_page; /* its inode page, NULL is possible */ - struct page *node_page; /* cached direct node page */ - nid_t nid; /* node id of the direct node block */ - unsigned int ofs_in_node; /* data offset in the node page */ - bool inode_page_locked; /* inode page is locked or not */ - block_t data_blkaddr; /* block address of the node block */ -}; - -static inline void set_new_dnode(struct dnode_of_data *dn, struct inode *inode, - struct page *ipage, struct page *npage, nid_t nid) -{ - dn->inode = inode; - dn->inode_page = ipage; - dn->node_page = npage; - dn->nid = nid; - dn->inode_page_locked = 0; -} - -/* - * For SIT manager - * - * By default, there are 6 active log areas across the whole main area. - * When considering hot and cold data separation to reduce cleaning overhead, - * we split 3 for data logs and 3 for node logs as hot, warm, and cold types, - * respectively. - * In the current design, you should not change the numbers intentionally. - * Instead, as a mount option such as active_logs=x, you can use 2, 4, and 6 - * logs individually according to the underlying devices. (default: 6) - * Just in case, on-disk layout covers maximum 16 logs that consist of 8 for - * data and 8 for node logs. - */ -#define NR_CURSEG_DATA_TYPE (3) -#define NR_CURSEG_NODE_TYPE (3) -#define NR_CURSEG_TYPE (NR_CURSEG_DATA_TYPE + NR_CURSEG_NODE_TYPE) - -enum { - CURSEG_HOT_DATA = 0, /* directory entry blocks */ - CURSEG_WARM_DATA, /* data blocks */ - CURSEG_COLD_DATA, /* multimedia or GCed data blocks */ - CURSEG_HOT_NODE, /* direct node blocks of directory files */ - CURSEG_WARM_NODE, /* direct node blocks of normal files */ - CURSEG_COLD_NODE, /* indirect node blocks */ - NO_CHECK_TYPE -}; - -struct f2fs_sm_info { - struct sit_info *sit_info; /* whole segment information */ - struct free_segmap_info *free_info; /* free segment information */ - struct dirty_seglist_info *dirty_info; /* dirty segment information */ - struct curseg_info *curseg_array; /* active segment information */ - - struct list_head wblist_head; /* list of under-writeback pages */ - spinlock_t wblist_lock; /* lock for checkpoint */ - - block_t seg0_blkaddr; /* block address of 0'th segment */ - block_t main_blkaddr; /* start block address of main area */ - block_t ssa_blkaddr; /* start block address of SSA area */ - - unsigned int segment_count; /* total # of segments */ - unsigned int main_segments; /* # of segments in main area */ - unsigned int reserved_segments; /* # of reserved segments */ - unsigned int ovp_segments; /* # of overprovision segments */ -}; - -/* - * For directory operation - */ -#define NODE_DIR1_BLOCK (ADDRS_PER_INODE + 1) -#define NODE_DIR2_BLOCK (ADDRS_PER_INODE + 2) -#define NODE_IND1_BLOCK (ADDRS_PER_INODE + 3) -#define NODE_IND2_BLOCK (ADDRS_PER_INODE + 4) -#define NODE_DIND_BLOCK (ADDRS_PER_INODE + 5) - -/* - * For superblock - */ -/* - * COUNT_TYPE for monitoring - * - * f2fs monitors the number of several block types such as on-writeback, - * dirty dentry blocks, dirty node blocks, and dirty meta blocks. - */ -enum count_type { - F2FS_WRITEBACK, - F2FS_DIRTY_DENTS, - F2FS_DIRTY_NODES, - F2FS_DIRTY_META, - NR_COUNT_TYPE, -}; - -/* - * FS_LOCK nesting subclasses for the lock validator: - * - * The locking order between these classes is - * RENAME -> DENTRY_OPS -> DATA_WRITE -> DATA_NEW - * -> DATA_TRUNC -> NODE_WRITE -> NODE_NEW -> NODE_TRUNC - */ -enum lock_type { - RENAME, /* for renaming operations */ - DENTRY_OPS, /* for directory operations */ - DATA_WRITE, /* for data write */ - DATA_NEW, /* for data allocation */ - DATA_TRUNC, /* for data truncate */ - NODE_NEW, /* for node allocation */ - NODE_TRUNC, /* for node truncate */ - NODE_WRITE, /* for node write */ - NR_LOCK_TYPE, -}; - -/* - * The below are the page types of bios used in submti_bio(). - * The available types are: - * DATA User data pages. It operates as async mode. - * NODE Node pages. It operates as async mode. - * META FS metadata pages such as SIT, NAT, CP. - * NR_PAGE_TYPE The number of page types. - * META_FLUSH Make sure the previous pages are written - * with waiting the bio's completion - * ... Only can be used with META. - */ -enum page_type { - DATA, - NODE, - META, - NR_PAGE_TYPE, - META_FLUSH, -}; - -struct f2fs_sb_info { - struct super_block *sb; /* pointer to VFS super block */ - struct buffer_head *raw_super_buf; /* buffer head of raw sb */ - struct f2fs_super_block *raw_super; /* raw super block pointer */ - int s_dirty; /* dirty flag for checkpoint */ - - /* for node-related operations */ - struct f2fs_nm_info *nm_info; /* node manager */ - struct inode *node_inode; /* cache node blocks */ - - /* for segment-related operations */ - struct f2fs_sm_info *sm_info; /* segment manager */ - struct bio *bio[NR_PAGE_TYPE]; /* bios to merge */ - sector_t last_block_in_bio[NR_PAGE_TYPE]; /* last block number */ - struct rw_semaphore bio_sem; /* IO semaphore */ - - /* for checkpoint */ - struct f2fs_checkpoint *ckpt; /* raw checkpoint pointer */ - struct inode *meta_inode; /* cache meta blocks */ - struct mutex cp_mutex; /* for checkpoint procedure */ - struct mutex fs_lock[NR_LOCK_TYPE]; /* for blocking FS operations */ - struct mutex write_inode; /* mutex for write inode */ - struct mutex writepages; /* mutex for writepages() */ - int por_doing; /* recovery is doing or not */ - - /* for orphan inode management */ - struct list_head orphan_inode_list; /* orphan inode list */ - struct mutex orphan_inode_mutex; /* for orphan inode list */ - unsigned int n_orphans; /* # of orphan inodes */ - - /* for directory inode management */ - struct list_head dir_inode_list; /* dir inode list */ - spinlock_t dir_inode_lock; /* for dir inode list lock */ - unsigned int n_dirty_dirs; /* # of dir inodes */ - - /* basic file system units */ - unsigned int log_sectors_per_block; /* log2 sectors per block */ - unsigned int log_blocksize; /* log2 block size */ - unsigned int blocksize; /* block size */ - unsigned int root_ino_num; /* root inode number*/ - unsigned int node_ino_num; /* node inode number*/ - unsigned int meta_ino_num; /* meta inode number*/ - unsigned int log_blocks_per_seg; /* log2 blocks per segment */ - unsigned int blocks_per_seg; /* blocks per segment */ - unsigned int segs_per_sec; /* segments per section */ - unsigned int secs_per_zone; /* sections per zone */ - unsigned int total_sections; /* total section count */ - unsigned int total_node_count; /* total node block count */ - unsigned int total_valid_node_count; /* valid node block count */ - unsigned int total_valid_inode_count; /* valid inode count */ - int active_logs; /* # of active logs */ - - block_t user_block_count; /* # of user blocks */ - block_t total_valid_block_count; /* # of valid blocks */ - block_t alloc_valid_block_count; /* # of allocated blocks */ - block_t last_valid_block_count; /* for recovery */ - u32 s_next_generation; /* for NFS support */ - atomic_t nr_pages[NR_COUNT_TYPE]; /* # of pages, see count_type */ - - struct f2fs_mount_info mount_opt; /* mount options */ - - /* for cleaning operations */ - struct mutex gc_mutex; /* mutex for GC */ - struct f2fs_gc_kthread *gc_thread; /* GC thread */ - - /* - * for stat information. - * one is for the LFS mode, and the other is for the SSR mode. - */ - struct f2fs_stat_info *stat_info; /* FS status information */ - unsigned int segment_count[2]; /* # of allocated segments */ - unsigned int block_count[2]; /* # of allocated blocks */ - unsigned int last_victim[2]; /* last victim segment # */ - int total_hit_ext, read_hit_ext; /* extent cache hit ratio */ - int bg_gc; /* background gc calls */ - spinlock_t stat_lock; /* lock for stat operations */ -}; - -/* - * Inline functions - */ -static inline struct f2fs_inode_info *F2FS_I(struct inode *inode) -{ - return container_of(inode, struct f2fs_inode_info, vfs_inode); -} - -static inline struct f2fs_sb_info *F2FS_SB(struct super_block *sb) -{ - return sb->s_fs_info; -} - -static inline struct f2fs_super_block *F2FS_RAW_SUPER(struct f2fs_sb_info *sbi) -{ - return (struct f2fs_super_block *)(sbi->raw_super); -} - -static inline struct f2fs_checkpoint *F2FS_CKPT(struct f2fs_sb_info *sbi) -{ - return (struct f2fs_checkpoint *)(sbi->ckpt); -} - -static inline struct f2fs_nm_info *NM_I(struct f2fs_sb_info *sbi) -{ - return (struct f2fs_nm_info *)(sbi->nm_info); -} - -static inline struct f2fs_sm_info *SM_I(struct f2fs_sb_info *sbi) -{ - return (struct f2fs_sm_info *)(sbi->sm_info); -} - -static inline struct sit_info *SIT_I(struct f2fs_sb_info *sbi) -{ - return (struct sit_info *)(SM_I(sbi)->sit_info); -} - -static inline struct free_segmap_info *FREE_I(struct f2fs_sb_info *sbi) -{ - return (struct free_segmap_info *)(SM_I(sbi)->free_info); -} - -static inline struct dirty_seglist_info *DIRTY_I(struct f2fs_sb_info *sbi) -{ - return (struct dirty_seglist_info *)(SM_I(sbi)->dirty_info); -} - -static inline void F2FS_SET_SB_DIRT(struct f2fs_sb_info *sbi) -{ - sbi->s_dirty = 1; -} - -static inline void F2FS_RESET_SB_DIRT(struct f2fs_sb_info *sbi) -{ - sbi->s_dirty = 0; -} - -static inline bool is_set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) -{ - unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); - return ckpt_flags & f; -} - -static inline void set_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) -{ - unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); - ckpt_flags |= f; - cp->ckpt_flags = cpu_to_le32(ckpt_flags); -} - -static inline void clear_ckpt_flags(struct f2fs_checkpoint *cp, unsigned int f) -{ - unsigned int ckpt_flags = le32_to_cpu(cp->ckpt_flags); - ckpt_flags &= (~f); - cp->ckpt_flags = cpu_to_le32(ckpt_flags); -} - -static inline void mutex_lock_op(struct f2fs_sb_info *sbi, enum lock_type t) -{ - mutex_lock_nested(&sbi->fs_lock[t], t); -} - -static inline void mutex_unlock_op(struct f2fs_sb_info *sbi, enum lock_type t) -{ - mutex_unlock(&sbi->fs_lock[t]); -} - -/* - * Check whether the given nid is within node id range. - */ -static inline void check_nid_range(struct f2fs_sb_info *sbi, nid_t nid) -{ - BUG_ON((nid >= NM_I(sbi)->max_nid)); -} - -#define F2FS_DEFAULT_ALLOCATED_BLOCKS 1 - -/* - * Check whether the inode has blocks or not - */ -static inline int F2FS_HAS_BLOCKS(struct inode *inode) -{ - if (F2FS_I(inode)->i_xattr_nid) - return (inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS + 1); - else - return (inode->i_blocks > F2FS_DEFAULT_ALLOCATED_BLOCKS); -} - -static inline bool inc_valid_block_count(struct f2fs_sb_info *sbi, - struct inode *inode, blkcnt_t count) -{ - block_t valid_block_count; - - spin_lock(&sbi->stat_lock); - valid_block_count = - sbi->total_valid_block_count + (block_t)count; - if (valid_block_count > sbi->user_block_count) { - spin_unlock(&sbi->stat_lock); - return false; - } - inode->i_blocks += count; - sbi->total_valid_block_count = valid_block_count; - sbi->alloc_valid_block_count += (block_t)count; - spin_unlock(&sbi->stat_lock); - return true; -} - -static inline int dec_valid_block_count(struct f2fs_sb_info *sbi, - struct inode *inode, - blkcnt_t count) -{ - spin_lock(&sbi->stat_lock); - BUG_ON(sbi->total_valid_block_count < (block_t) count); - BUG_ON(inode->i_blocks < count); - inode->i_blocks -= count; - sbi->total_valid_block_count -= (block_t)count; - spin_unlock(&sbi->stat_lock); - return 0; -} - -static inline void inc_page_count(struct f2fs_sb_info *sbi, int count_type) -{ - atomic_inc(&sbi->nr_pages[count_type]); - F2FS_SET_SB_DIRT(sbi); -} - -static inline void inode_inc_dirty_dents(struct inode *inode) -{ - atomic_inc(&F2FS_I(inode)->dirty_dents); -} - -static inline void dec_page_count(struct f2fs_sb_info *sbi, int count_type) -{ - atomic_dec(&sbi->nr_pages[count_type]); -} - -static inline void inode_dec_dirty_dents(struct inode *inode) -{ - atomic_dec(&F2FS_I(inode)->dirty_dents); -} - -static inline int get_pages(struct f2fs_sb_info *sbi, int count_type) -{ - return atomic_read(&sbi->nr_pages[count_type]); -} - -static inline block_t valid_user_blocks(struct f2fs_sb_info *sbi) -{ - block_t ret; - spin_lock(&sbi->stat_lock); - ret = sbi->total_valid_block_count; - spin_unlock(&sbi->stat_lock); - return ret; -} - -static inline unsigned long __bitmap_size(struct f2fs_sb_info *sbi, int flag) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - - /* return NAT or SIT bitmap */ - if (flag == NAT_BITMAP) - return le32_to_cpu(ckpt->nat_ver_bitmap_bytesize); - else if (flag == SIT_BITMAP) - return le32_to_cpu(ckpt->sit_ver_bitmap_bytesize); - - return 0; -} - -static inline void *__bitmap_ptr(struct f2fs_sb_info *sbi, int flag) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - int offset = (flag == NAT_BITMAP) ? - le32_to_cpu(ckpt->sit_ver_bitmap_bytesize) : 0; - return &ckpt->sit_nat_version_bitmap + offset; -} - -static inline block_t __start_cp_addr(struct f2fs_sb_info *sbi) -{ - block_t start_addr; - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - unsigned long long ckpt_version = le64_to_cpu(ckpt->checkpoint_ver); - - start_addr = le32_to_cpu(F2FS_RAW_SUPER(sbi)->cp_blkaddr); - - /* - * odd numbered checkpoint should at cp segment 0 - * and even segent must be at cp segment 1 - */ - if (!(ckpt_version & 1)) - start_addr += sbi->blocks_per_seg; - - return start_addr; -} - -static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) -{ - return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); -} - -static inline bool inc_valid_node_count(struct f2fs_sb_info *sbi, - struct inode *inode, - unsigned int count) -{ - block_t valid_block_count; - unsigned int valid_node_count; - - spin_lock(&sbi->stat_lock); - - valid_block_count = sbi->total_valid_block_count + (block_t)count; - sbi->alloc_valid_block_count += (block_t)count; - valid_node_count = sbi->total_valid_node_count + count; - - if (valid_block_count > sbi->user_block_count) { - spin_unlock(&sbi->stat_lock); - return false; - } - - if (valid_node_count > sbi->total_node_count) { - spin_unlock(&sbi->stat_lock); - return false; - } - - if (inode) - inode->i_blocks += count; - sbi->total_valid_node_count = valid_node_count; - sbi->total_valid_block_count = valid_block_count; - spin_unlock(&sbi->stat_lock); - - return true; -} - -static inline void dec_valid_node_count(struct f2fs_sb_info *sbi, - struct inode *inode, - unsigned int count) -{ - spin_lock(&sbi->stat_lock); - - BUG_ON(sbi->total_valid_block_count < count); - BUG_ON(sbi->total_valid_node_count < count); - BUG_ON(inode->i_blocks < count); - - inode->i_blocks -= count; - sbi->total_valid_node_count -= count; - sbi->total_valid_block_count -= (block_t)count; - - spin_unlock(&sbi->stat_lock); -} - -static inline unsigned int valid_node_count(struct f2fs_sb_info *sbi) -{ - unsigned int ret; - spin_lock(&sbi->stat_lock); - ret = sbi->total_valid_node_count; - spin_unlock(&sbi->stat_lock); - return ret; -} - -static inline void inc_valid_inode_count(struct f2fs_sb_info *sbi) -{ - spin_lock(&sbi->stat_lock); - BUG_ON(sbi->total_valid_inode_count == sbi->total_node_count); - sbi->total_valid_inode_count++; - spin_unlock(&sbi->stat_lock); -} - -static inline int dec_valid_inode_count(struct f2fs_sb_info *sbi) -{ - spin_lock(&sbi->stat_lock); - BUG_ON(!sbi->total_valid_inode_count); - sbi->total_valid_inode_count--; - spin_unlock(&sbi->stat_lock); - return 0; -} - -static inline unsigned int valid_inode_count(struct f2fs_sb_info *sbi) -{ - unsigned int ret; - spin_lock(&sbi->stat_lock); - ret = sbi->total_valid_inode_count; - spin_unlock(&sbi->stat_lock); - return ret; -} - -static inline void f2fs_put_page(struct page *page, int unlock) -{ - if (!page || IS_ERR(page)) - return; - - if (unlock) { - BUG_ON(!PageLocked(page)); - unlock_page(page); - } - page_cache_release(page); -} - -static inline void f2fs_put_dnode(struct dnode_of_data *dn) -{ - if (dn->node_page) - f2fs_put_page(dn->node_page, 1); - if (dn->inode_page && dn->node_page != dn->inode_page) - f2fs_put_page(dn->inode_page, 0); - dn->node_page = NULL; - dn->inode_page = NULL; -} - -static inline struct kmem_cache *f2fs_kmem_cache_create(const char *name, - size_t size, void (*ctor)(void *)) -{ - return kmem_cache_create(name, size, 0, SLAB_RECLAIM_ACCOUNT, ctor); -} - -#define RAW_IS_INODE(p) ((p)->footer.nid == (p)->footer.ino) - -static inline bool IS_INODE(struct page *page) -{ - struct f2fs_node *p = (struct f2fs_node *)page_address(page); - return RAW_IS_INODE(p); -} - -static inline __le32 *blkaddr_in_node(struct f2fs_node *node) -{ - return RAW_IS_INODE(node) ? node->i.i_addr : node->dn.addr; -} - -static inline block_t datablock_addr(struct page *node_page, - unsigned int offset) -{ - struct f2fs_node *raw_node; - __le32 *addr_array; - raw_node = (struct f2fs_node *)page_address(node_page); - addr_array = blkaddr_in_node(raw_node); - return le32_to_cpu(addr_array[offset]); -} - -static inline int f2fs_test_bit(unsigned int nr, char *addr) -{ - int mask; - - addr += (nr >> 3); - mask = 1 << (7 - (nr & 0x07)); - return mask & *addr; -} - -static inline int f2fs_set_bit(unsigned int nr, char *addr) -{ - int mask; - int ret; - - addr += (nr >> 3); - mask = 1 << (7 - (nr & 0x07)); - ret = mask & *addr; - *addr |= mask; - return ret; -} - -static inline int f2fs_clear_bit(unsigned int nr, char *addr) -{ - int mask; - int ret; - - addr += (nr >> 3); - mask = 1 << (7 - (nr & 0x07)); - ret = mask & *addr; - *addr &= ~mask; - return ret; -} - -/* used for f2fs_inode_info->flags */ -enum { - FI_NEW_INODE, /* indicate newly allocated inode */ - FI_NEED_CP, /* need to do checkpoint during fsync */ - FI_INC_LINK, /* need to increment i_nlink */ - FI_ACL_MODE, /* indicate acl mode */ - FI_NO_ALLOC, /* should not allocate any blocks */ -}; - -static inline void set_inode_flag(struct f2fs_inode_info *fi, int flag) -{ - set_bit(flag, &fi->flags); -} - -static inline int is_inode_flag_set(struct f2fs_inode_info *fi, int flag) -{ - return test_bit(flag, &fi->flags); -} - -static inline void clear_inode_flag(struct f2fs_inode_info *fi, int flag) -{ - clear_bit(flag, &fi->flags); -} - -static inline void set_acl_inode(struct f2fs_inode_info *fi, umode_t mode) -{ - fi->i_acl_mode = mode; - set_inode_flag(fi, FI_ACL_MODE); -} - -static inline int cond_clear_inode_flag(struct f2fs_inode_info *fi, int flag) -{ - if (is_inode_flag_set(fi, FI_ACL_MODE)) { - clear_inode_flag(fi, FI_ACL_MODE); - return 1; - } - return 0; -} - -/* - * file.c - */ -int f2fs_sync_file(struct file *, loff_t, loff_t, int); -void truncate_data_blocks(struct dnode_of_data *); -void f2fs_truncate(struct inode *); -int f2fs_setattr(struct dentry *, struct iattr *); -int truncate_hole(struct inode *, pgoff_t, pgoff_t); -long f2fs_ioctl(struct file *, unsigned int, unsigned long); - -/* - * inode.c - */ -void f2fs_set_inode_flags(struct inode *); -struct inode *f2fs_iget_nowait(struct super_block *, unsigned long); -struct inode *f2fs_iget(struct super_block *, unsigned long); -void update_inode(struct inode *, struct page *); -int f2fs_write_inode(struct inode *, struct writeback_control *); -void f2fs_evict_inode(struct inode *); - -/* - * namei.c - */ -struct dentry *f2fs_get_parent(struct dentry *child); - -/* - * dir.c - */ -struct f2fs_dir_entry *f2fs_find_entry(struct inode *, struct qstr *, - struct page **); -struct f2fs_dir_entry *f2fs_parent_dir(struct inode *, struct page **); -ino_t f2fs_inode_by_name(struct inode *, struct qstr *); -void f2fs_set_link(struct inode *, struct f2fs_dir_entry *, - struct page *, struct inode *); -void init_dent_inode(struct dentry *, struct page *); -int f2fs_add_link(struct dentry *, struct inode *); -void f2fs_delete_entry(struct f2fs_dir_entry *, struct page *, struct inode *); -int f2fs_make_empty(struct inode *, struct inode *); -bool f2fs_empty_dir(struct inode *); - -/* - * super.c - */ -int f2fs_sync_fs(struct super_block *, int); - -/* - * hash.c - */ -f2fs_hash_t f2fs_dentry_hash(const char *, int); - -/* - * node.c - */ -struct dnode_of_data; -struct node_info; - -int is_checkpointed_node(struct f2fs_sb_info *, nid_t); -void get_node_info(struct f2fs_sb_info *, nid_t, struct node_info *); -int get_dnode_of_data(struct dnode_of_data *, pgoff_t, int); -int truncate_inode_blocks(struct inode *, pgoff_t); -int remove_inode_page(struct inode *); -int new_inode_page(struct inode *, struct dentry *); -struct page *new_node_page(struct dnode_of_data *, unsigned int); -void ra_node_page(struct f2fs_sb_info *, nid_t); -struct page *get_node_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_node_page_ra(struct page *, int); -void sync_inode_page(struct dnode_of_data *); -int sync_node_pages(struct f2fs_sb_info *, nid_t, struct writeback_control *); -bool alloc_nid(struct f2fs_sb_info *, nid_t *); -void alloc_nid_done(struct f2fs_sb_info *, nid_t); -void alloc_nid_failed(struct f2fs_sb_info *, nid_t); -void recover_node_page(struct f2fs_sb_info *, struct page *, - struct f2fs_summary *, struct node_info *, block_t); -int recover_inode_page(struct f2fs_sb_info *, struct page *); -int restore_node_summary(struct f2fs_sb_info *, unsigned int, - struct f2fs_summary_block *); -void flush_nat_entries(struct f2fs_sb_info *); -int build_node_manager(struct f2fs_sb_info *); -void destroy_node_manager(struct f2fs_sb_info *); -int create_node_manager_caches(void); -void destroy_node_manager_caches(void); - -/* - * segment.c - */ -void f2fs_balance_fs(struct f2fs_sb_info *); -void invalidate_blocks(struct f2fs_sb_info *, block_t); -void locate_dirty_segment(struct f2fs_sb_info *, unsigned int); -void clear_prefree_segments(struct f2fs_sb_info *); -int npages_for_summary_flush(struct f2fs_sb_info *); -void allocate_new_segments(struct f2fs_sb_info *); -struct page *get_sum_page(struct f2fs_sb_info *, unsigned int); -struct bio *f2fs_bio_alloc(struct block_device *, int); -void f2fs_submit_bio(struct f2fs_sb_info *, enum page_type, bool sync); -int write_meta_page(struct f2fs_sb_info *, struct page *, - struct writeback_control *); -void write_node_page(struct f2fs_sb_info *, struct page *, unsigned int, - block_t, block_t *); -void write_data_page(struct inode *, struct page *, struct dnode_of_data*, - block_t, block_t *); -void rewrite_data_page(struct f2fs_sb_info *, struct page *, block_t); -void recover_data_page(struct f2fs_sb_info *, struct page *, - struct f2fs_summary *, block_t, block_t); -void rewrite_node_page(struct f2fs_sb_info *, struct page *, - struct f2fs_summary *, block_t, block_t); -void write_data_summaries(struct f2fs_sb_info *, block_t); -void write_node_summaries(struct f2fs_sb_info *, block_t); -int lookup_journal_in_cursum(struct f2fs_summary_block *, - int, unsigned int, int); -void flush_sit_entries(struct f2fs_sb_info *); -int build_segment_manager(struct f2fs_sb_info *); -void reset_victim_segmap(struct f2fs_sb_info *); -void destroy_segment_manager(struct f2fs_sb_info *); - -/* - * checkpoint.c - */ -struct page *grab_meta_page(struct f2fs_sb_info *, pgoff_t); -struct page *get_meta_page(struct f2fs_sb_info *, pgoff_t); -long sync_meta_pages(struct f2fs_sb_info *, enum page_type, long); -int check_orphan_space(struct f2fs_sb_info *); -void add_orphan_inode(struct f2fs_sb_info *, nid_t); -void remove_orphan_inode(struct f2fs_sb_info *, nid_t); -int recover_orphan_inodes(struct f2fs_sb_info *); -int get_valid_checkpoint(struct f2fs_sb_info *); -void set_dirty_dir_page(struct inode *, struct page *); -void remove_dirty_dir_inode(struct inode *); -void sync_dirty_dir_inodes(struct f2fs_sb_info *); -void block_operations(struct f2fs_sb_info *); -void write_checkpoint(struct f2fs_sb_info *, bool, bool); -void init_orphan_info(struct f2fs_sb_info *); -int create_checkpoint_caches(void); -void destroy_checkpoint_caches(void); - -/* - * data.c - */ -int reserve_new_block(struct dnode_of_data *); -void update_extent_cache(block_t, struct dnode_of_data *); -struct page *find_data_page(struct inode *, pgoff_t); -struct page *get_lock_data_page(struct inode *, pgoff_t); -struct page *get_new_data_page(struct inode *, pgoff_t, bool); -int f2fs_readpage(struct f2fs_sb_info *, struct page *, block_t, int); -int do_write_data_page(struct page *); - -/* - * gc.c - */ -int start_gc_thread(struct f2fs_sb_info *); -void stop_gc_thread(struct f2fs_sb_info *); -block_t start_bidx_of_node(unsigned int); -int f2fs_gc(struct f2fs_sb_info *, int); -void build_gc_manager(struct f2fs_sb_info *); -int create_gc_caches(void); -void destroy_gc_caches(void); - -/* - * recovery.c - */ -void recover_fsync_data(struct f2fs_sb_info *); -bool space_for_roll_forward(struct f2fs_sb_info *); - -/* - * debug.c - */ -#ifdef CONFIG_F2FS_STAT_FS -struct f2fs_stat_info { - struct list_head stat_list; - struct f2fs_sb_info *sbi; - struct mutex stat_lock; - int all_area_segs, sit_area_segs, nat_area_segs, ssa_area_segs; - int main_area_segs, main_area_sections, main_area_zones; - int hit_ext, total_ext; - int ndirty_node, ndirty_dent, ndirty_dirs, ndirty_meta; - int nats, sits, fnids; - int total_count, utilization; - int bg_gc; - unsigned int valid_count, valid_node_count, valid_inode_count; - unsigned int bimodal, avg_vblocks; - int util_free, util_valid, util_invalid; - int rsvd_segs, overp_segs; - int dirty_count, node_pages, meta_pages; - int prefree_count, call_count; - int tot_segs, node_segs, data_segs, free_segs, free_secs; - int tot_blks, data_blks, node_blks; - int curseg[NR_CURSEG_TYPE]; - int cursec[NR_CURSEG_TYPE]; - int curzone[NR_CURSEG_TYPE]; - - unsigned int segment_count[2]; - unsigned int block_count[2]; - unsigned base_mem, cache_mem; -}; - -#define stat_inc_call_count(si) ((si)->call_count++) - -#define stat_inc_seg_count(sbi, type) \ - do { \ - struct f2fs_stat_info *si = sbi->stat_info; \ - (si)->tot_segs++; \ - if (type == SUM_TYPE_DATA) \ - si->data_segs++; \ - else \ - si->node_segs++; \ - } while (0) - -#define stat_inc_tot_blk_count(si, blks) \ - (si->tot_blks += (blks)) - -#define stat_inc_data_blk_count(sbi, blks) \ - do { \ - struct f2fs_stat_info *si = sbi->stat_info; \ - stat_inc_tot_blk_count(si, blks); \ - si->data_blks += (blks); \ - } while (0) - -#define stat_inc_node_blk_count(sbi, blks) \ - do { \ - struct f2fs_stat_info *si = sbi->stat_info; \ - stat_inc_tot_blk_count(si, blks); \ - si->node_blks += (blks); \ - } while (0) - -int f2fs_build_stats(struct f2fs_sb_info *); -void f2fs_destroy_stats(struct f2fs_sb_info *); -void destroy_root_stats(void); -#else -#define stat_inc_call_count(si) -#define stat_inc_seg_count(si, type) -#define stat_inc_tot_blk_count(si, blks) -#define stat_inc_data_blk_count(si, blks) -#define stat_inc_node_blk_count(sbi, blks) - -static inline int f2fs_build_stats(struct f2fs_sb_info *sbi) { return 0; } -static inline void f2fs_destroy_stats(struct f2fs_sb_info *sbi) { } -static inline void destroy_root_stats(void) { } -#endif - -extern const struct file_operations f2fs_dir_operations; -extern const struct file_operations f2fs_file_operations; -extern const struct inode_operations f2fs_file_inode_operations; -extern const struct address_space_operations f2fs_dblock_aops; -extern const struct address_space_operations f2fs_node_aops; -extern const struct address_space_operations f2fs_meta_aops; -extern const struct inode_operations f2fs_dir_inode_operations; -extern const struct inode_operations f2fs_symlink_inode_operations; -extern const struct inode_operations f2fs_special_inode_operations; -#endif diff --git a/trunk/fs/f2fs/file.c b/trunk/fs/f2fs/file.c deleted file mode 100644 index f9e085dfb1f0..000000000000 --- a/trunk/fs/f2fs/file.c +++ /dev/null @@ -1,636 +0,0 @@ -/* - * fs/f2fs/file.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" -#include "xattr.h" -#include "acl.h" - -static int f2fs_vm_page_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) -{ - struct page *page = vmf->page; - struct inode *inode = vma->vm_file->f_path.dentry->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - block_t old_blk_addr; - struct dnode_of_data dn; - int err; - - f2fs_balance_fs(sbi); - - sb_start_pagefault(inode->i_sb); - - mutex_lock_op(sbi, DATA_NEW); - - /* block allocation */ - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, page->index, 0); - if (err) { - mutex_unlock_op(sbi, DATA_NEW); - goto out; - } - - old_blk_addr = dn.data_blkaddr; - - if (old_blk_addr == NULL_ADDR) { - err = reserve_new_block(&dn); - if (err) { - f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); - goto out; - } - } - f2fs_put_dnode(&dn); - - mutex_unlock_op(sbi, DATA_NEW); - - lock_page(page); - if (page->mapping != inode->i_mapping || - page_offset(page) >= i_size_read(inode) || - !PageUptodate(page)) { - unlock_page(page); - err = -EFAULT; - goto out; - } - - /* - * check to see if the page is mapped already (no holes) - */ - if (PageMappedToDisk(page)) - goto out; - - /* fill the page */ - wait_on_page_writeback(page); - - /* page is wholly or partially inside EOF */ - if (((page->index + 1) << PAGE_CACHE_SHIFT) > i_size_read(inode)) { - unsigned offset; - offset = i_size_read(inode) & ~PAGE_CACHE_MASK; - zero_user_segment(page, offset, PAGE_CACHE_SIZE); - } - set_page_dirty(page); - SetPageUptodate(page); - - file_update_time(vma->vm_file); -out: - sb_end_pagefault(inode->i_sb); - return block_page_mkwrite_return(err); -} - -static const struct vm_operations_struct f2fs_file_vm_ops = { - .fault = filemap_fault, - .page_mkwrite = f2fs_vm_page_mkwrite, -}; - -static int need_to_sync_dir(struct f2fs_sb_info *sbi, struct inode *inode) -{ - struct dentry *dentry; - nid_t pino; - - inode = igrab(inode); - dentry = d_find_any_alias(inode); - if (!dentry) { - iput(inode); - return 0; - } - pino = dentry->d_parent->d_inode->i_ino; - dput(dentry); - iput(inode); - return !is_checkpointed_node(sbi, pino); -} - -int f2fs_sync_file(struct file *file, loff_t start, loff_t end, int datasync) -{ - struct inode *inode = file->f_mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - unsigned long long cur_version; - int ret = 0; - bool need_cp = false; - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - - if (inode->i_sb->s_flags & MS_RDONLY) - return 0; - - ret = filemap_write_and_wait_range(inode->i_mapping, start, end); - if (ret) - return ret; - - mutex_lock(&inode->i_mutex); - - if (datasync && !(inode->i_state & I_DIRTY_DATASYNC)) - goto out; - - mutex_lock(&sbi->cp_mutex); - cur_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver); - mutex_unlock(&sbi->cp_mutex); - - if (F2FS_I(inode)->data_version != cur_version && - !(inode->i_state & I_DIRTY)) - goto out; - F2FS_I(inode)->data_version--; - - if (!S_ISREG(inode->i_mode) || inode->i_nlink != 1) - need_cp = true; - if (is_inode_flag_set(F2FS_I(inode), FI_NEED_CP)) - need_cp = true; - if (!space_for_roll_forward(sbi)) - need_cp = true; - if (need_to_sync_dir(sbi, inode)) - need_cp = true; - - f2fs_write_inode(inode, NULL); - - if (need_cp) { - /* all the dirty node pages should be flushed for POR */ - ret = f2fs_sync_fs(inode->i_sb, 1); - clear_inode_flag(F2FS_I(inode), FI_NEED_CP); - } else { - while (sync_node_pages(sbi, inode->i_ino, &wbc) == 0) - f2fs_write_inode(inode, NULL); - filemap_fdatawait_range(sbi->node_inode->i_mapping, - 0, LONG_MAX); - } -out: - mutex_unlock(&inode->i_mutex); - return ret; -} - -static int f2fs_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - file_accessed(file); - vma->vm_ops = &f2fs_file_vm_ops; - return 0; -} - -static int truncate_data_blocks_range(struct dnode_of_data *dn, int count) -{ - int nr_free = 0, ofs = dn->ofs_in_node; - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct f2fs_node *raw_node; - __le32 *addr; - - raw_node = page_address(dn->node_page); - addr = blkaddr_in_node(raw_node) + ofs; - - for ( ; count > 0; count--, addr++, dn->ofs_in_node++) { - block_t blkaddr = le32_to_cpu(*addr); - if (blkaddr == NULL_ADDR) - continue; - - update_extent_cache(NULL_ADDR, dn); - invalidate_blocks(sbi, blkaddr); - dec_valid_block_count(sbi, dn->inode, 1); - nr_free++; - } - if (nr_free) { - set_page_dirty(dn->node_page); - sync_inode_page(dn); - } - dn->ofs_in_node = ofs; - return nr_free; -} - -void truncate_data_blocks(struct dnode_of_data *dn) -{ - truncate_data_blocks_range(dn, ADDRS_PER_BLOCK); -} - -static void truncate_partial_data_page(struct inode *inode, u64 from) -{ - unsigned offset = from & (PAGE_CACHE_SIZE - 1); - struct page *page; - - if (!offset) - return; - - page = find_data_page(inode, from >> PAGE_CACHE_SHIFT); - if (IS_ERR(page)) - return; - - lock_page(page); - wait_on_page_writeback(page); - zero_user(page, offset, PAGE_CACHE_SIZE - offset); - set_page_dirty(page); - f2fs_put_page(page, 1); -} - -static int truncate_blocks(struct inode *inode, u64 from) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - unsigned int blocksize = inode->i_sb->s_blocksize; - struct dnode_of_data dn; - pgoff_t free_from; - int count = 0; - int err; - - free_from = (pgoff_t) - ((from + blocksize - 1) >> (sbi->log_blocksize)); - - mutex_lock_op(sbi, DATA_TRUNC); - - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, free_from, RDONLY_NODE); - if (err) { - if (err == -ENOENT) - goto free_next; - mutex_unlock_op(sbi, DATA_TRUNC); - return err; - } - - if (IS_INODE(dn.node_page)) - count = ADDRS_PER_INODE; - else - count = ADDRS_PER_BLOCK; - - count -= dn.ofs_in_node; - BUG_ON(count < 0); - if (dn.ofs_in_node || IS_INODE(dn.node_page)) { - truncate_data_blocks_range(&dn, count); - free_from += count; - } - - f2fs_put_dnode(&dn); -free_next: - err = truncate_inode_blocks(inode, free_from); - mutex_unlock_op(sbi, DATA_TRUNC); - - /* lastly zero out the first data page */ - truncate_partial_data_page(inode, from); - - return err; -} - -void f2fs_truncate(struct inode *inode) -{ - if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || - S_ISLNK(inode->i_mode))) - return; - - if (!truncate_blocks(inode, i_size_read(inode))) { - inode->i_mtime = inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); - } - - f2fs_balance_fs(F2FS_SB(inode->i_sb)); -} - -static int f2fs_getattr(struct vfsmount *mnt, - struct dentry *dentry, struct kstat *stat) -{ - struct inode *inode = dentry->d_inode; - generic_fillattr(inode, stat); - stat->blocks <<= 3; - return 0; -} - -#ifdef CONFIG_F2FS_FS_POSIX_ACL -static void __setattr_copy(struct inode *inode, const struct iattr *attr) -{ - struct f2fs_inode_info *fi = F2FS_I(inode); - unsigned int ia_valid = attr->ia_valid; - - if (ia_valid & ATTR_UID) - inode->i_uid = attr->ia_uid; - if (ia_valid & ATTR_GID) - inode->i_gid = attr->ia_gid; - if (ia_valid & ATTR_ATIME) - inode->i_atime = timespec_trunc(attr->ia_atime, - inode->i_sb->s_time_gran); - if (ia_valid & ATTR_MTIME) - inode->i_mtime = timespec_trunc(attr->ia_mtime, - inode->i_sb->s_time_gran); - if (ia_valid & ATTR_CTIME) - inode->i_ctime = timespec_trunc(attr->ia_ctime, - inode->i_sb->s_time_gran); - if (ia_valid & ATTR_MODE) { - umode_t mode = attr->ia_mode; - - if (!in_group_p(inode->i_gid) && !capable(CAP_FSETID)) - mode &= ~S_ISGID; - set_acl_inode(fi, mode); - } -} -#else -#define __setattr_copy setattr_copy -#endif - -int f2fs_setattr(struct dentry *dentry, struct iattr *attr) -{ - struct inode *inode = dentry->d_inode; - struct f2fs_inode_info *fi = F2FS_I(inode); - int err; - - err = inode_change_ok(inode, attr); - if (err) - return err; - - if ((attr->ia_valid & ATTR_SIZE) && - attr->ia_size != i_size_read(inode)) { - truncate_setsize(inode, attr->ia_size); - f2fs_truncate(inode); - } - - __setattr_copy(inode, attr); - - if (attr->ia_valid & ATTR_MODE) { - err = f2fs_acl_chmod(inode); - if (err || is_inode_flag_set(fi, FI_ACL_MODE)) { - inode->i_mode = fi->i_acl_mode; - clear_inode_flag(fi, FI_ACL_MODE); - } - } - - mark_inode_dirty(inode); - return err; -} - -const struct inode_operations f2fs_file_inode_operations = { - .getattr = f2fs_getattr, - .setattr = f2fs_setattr, - .get_acl = f2fs_get_acl, -#ifdef CONFIG_F2FS_FS_XATTR - .setxattr = generic_setxattr, - .getxattr = generic_getxattr, - .listxattr = f2fs_listxattr, - .removexattr = generic_removexattr, -#endif -}; - -static void fill_zero(struct inode *inode, pgoff_t index, - loff_t start, loff_t len) -{ - struct page *page; - - if (!len) - return; - - page = get_new_data_page(inode, index, false); - - if (!IS_ERR(page)) { - wait_on_page_writeback(page); - zero_user(page, start, len); - set_page_dirty(page); - f2fs_put_page(page, 1); - } -} - -int truncate_hole(struct inode *inode, pgoff_t pg_start, pgoff_t pg_end) -{ - pgoff_t index; - int err; - - for (index = pg_start; index < pg_end; index++) { - struct dnode_of_data dn; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - - mutex_lock_op(sbi, DATA_TRUNC); - set_new_dnode(&dn, inode, NULL, NULL, 0); - err = get_dnode_of_data(&dn, index, RDONLY_NODE); - if (err) { - mutex_unlock_op(sbi, DATA_TRUNC); - if (err == -ENOENT) - continue; - return err; - } - - if (dn.data_blkaddr != NULL_ADDR) - truncate_data_blocks_range(&dn, 1); - f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_TRUNC); - } - return 0; -} - -static int punch_hole(struct inode *inode, loff_t offset, loff_t len, int mode) -{ - pgoff_t pg_start, pg_end; - loff_t off_start, off_end; - int ret = 0; - - pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; - pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; - - off_start = offset & (PAGE_CACHE_SIZE - 1); - off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); - - if (pg_start == pg_end) { - fill_zero(inode, pg_start, off_start, - off_end - off_start); - } else { - if (off_start) - fill_zero(inode, pg_start++, off_start, - PAGE_CACHE_SIZE - off_start); - if (off_end) - fill_zero(inode, pg_end, 0, off_end); - - if (pg_start < pg_end) { - struct address_space *mapping = inode->i_mapping; - loff_t blk_start, blk_end; - - blk_start = pg_start << PAGE_CACHE_SHIFT; - blk_end = pg_end << PAGE_CACHE_SHIFT; - truncate_inode_pages_range(mapping, blk_start, - blk_end - 1); - ret = truncate_hole(inode, pg_start, pg_end); - } - } - - if (!(mode & FALLOC_FL_KEEP_SIZE) && - i_size_read(inode) <= (offset + len)) { - i_size_write(inode, offset); - mark_inode_dirty(inode); - } - - return ret; -} - -static int expand_inode_data(struct inode *inode, loff_t offset, - loff_t len, int mode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - pgoff_t index, pg_start, pg_end; - loff_t new_size = i_size_read(inode); - loff_t off_start, off_end; - int ret = 0; - - ret = inode_newsize_ok(inode, (len + offset)); - if (ret) - return ret; - - pg_start = ((unsigned long long) offset) >> PAGE_CACHE_SHIFT; - pg_end = ((unsigned long long) offset + len) >> PAGE_CACHE_SHIFT; - - off_start = offset & (PAGE_CACHE_SIZE - 1); - off_end = (offset + len) & (PAGE_CACHE_SIZE - 1); - - for (index = pg_start; index <= pg_end; index++) { - struct dnode_of_data dn; - - mutex_lock_op(sbi, DATA_NEW); - - set_new_dnode(&dn, inode, NULL, NULL, 0); - ret = get_dnode_of_data(&dn, index, 0); - if (ret) { - mutex_unlock_op(sbi, DATA_NEW); - break; - } - - if (dn.data_blkaddr == NULL_ADDR) { - ret = reserve_new_block(&dn); - if (ret) { - f2fs_put_dnode(&dn); - mutex_unlock_op(sbi, DATA_NEW); - break; - } - } - f2fs_put_dnode(&dn); - - mutex_unlock_op(sbi, DATA_NEW); - - if (pg_start == pg_end) - new_size = offset + len; - else if (index == pg_start && off_start) - new_size = (index + 1) << PAGE_CACHE_SHIFT; - else if (index == pg_end) - new_size = (index << PAGE_CACHE_SHIFT) + off_end; - else - new_size += PAGE_CACHE_SIZE; - } - - if (!(mode & FALLOC_FL_KEEP_SIZE) && - i_size_read(inode) < new_size) { - i_size_write(inode, new_size); - mark_inode_dirty(inode); - } - - return ret; -} - -static long f2fs_fallocate(struct file *file, int mode, - loff_t offset, loff_t len) -{ - struct inode *inode = file->f_path.dentry->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - long ret; - - if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) - return -EOPNOTSUPP; - - if (mode & FALLOC_FL_PUNCH_HOLE) - ret = punch_hole(inode, offset, len, mode); - else - ret = expand_inode_data(inode, offset, len, mode); - - f2fs_balance_fs(sbi); - return ret; -} - -#define F2FS_REG_FLMASK (~(FS_DIRSYNC_FL | FS_TOPDIR_FL)) -#define F2FS_OTHER_FLMASK (FS_NODUMP_FL | FS_NOATIME_FL) - -static inline __u32 f2fs_mask_flags(umode_t mode, __u32 flags) -{ - if (S_ISDIR(mode)) - return flags; - else if (S_ISREG(mode)) - return flags & F2FS_REG_FLMASK; - else - return flags & F2FS_OTHER_FLMASK; -} - -long f2fs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) -{ - struct inode *inode = filp->f_dentry->d_inode; - struct f2fs_inode_info *fi = F2FS_I(inode); - unsigned int flags; - int ret; - - switch (cmd) { - case FS_IOC_GETFLAGS: - flags = fi->i_flags & FS_FL_USER_VISIBLE; - return put_user(flags, (int __user *) arg); - case FS_IOC_SETFLAGS: - { - unsigned int oldflags; - - ret = mnt_want_write(filp->f_path.mnt); - if (ret) - return ret; - - if (!inode_owner_or_capable(inode)) { - ret = -EACCES; - goto out; - } - - if (get_user(flags, (int __user *) arg)) { - ret = -EFAULT; - goto out; - } - - flags = f2fs_mask_flags(inode->i_mode, flags); - - mutex_lock(&inode->i_mutex); - - oldflags = fi->i_flags; - - if ((flags ^ oldflags) & (FS_APPEND_FL | FS_IMMUTABLE_FL)) { - if (!capable(CAP_LINUX_IMMUTABLE)) { - mutex_unlock(&inode->i_mutex); - ret = -EPERM; - goto out; - } - } - - flags = flags & FS_FL_USER_MODIFIABLE; - flags |= oldflags & ~FS_FL_USER_MODIFIABLE; - fi->i_flags = flags; - mutex_unlock(&inode->i_mutex); - - f2fs_set_inode_flags(inode); - inode->i_ctime = CURRENT_TIME; - mark_inode_dirty(inode); -out: - mnt_drop_write(filp->f_path.mnt); - return ret; - } - default: - return -ENOTTY; - } -} - -const struct file_operations f2fs_file_operations = { - .llseek = generic_file_llseek, - .read = do_sync_read, - .write = do_sync_write, - .aio_read = generic_file_aio_read, - .aio_write = generic_file_aio_write, - .open = generic_file_open, - .mmap = f2fs_file_mmap, - .fsync = f2fs_sync_file, - .fallocate = f2fs_fallocate, - .unlocked_ioctl = f2fs_ioctl, - .splice_read = generic_file_splice_read, - .splice_write = generic_file_splice_write, -}; diff --git a/trunk/fs/f2fs/gc.c b/trunk/fs/f2fs/gc.c deleted file mode 100644 index 644aa3808273..000000000000 --- a/trunk/fs/f2fs/gc.c +++ /dev/null @@ -1,742 +0,0 @@ -/* - * fs/f2fs/gc.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" -#include "gc.h" - -static struct kmem_cache *winode_slab; - -static int gc_thread_func(void *data) -{ - struct f2fs_sb_info *sbi = data; - wait_queue_head_t *wq = &sbi->gc_thread->gc_wait_queue_head; - long wait_ms; - - wait_ms = GC_THREAD_MIN_SLEEP_TIME; - - do { - if (try_to_freeze()) - continue; - else - wait_event_interruptible_timeout(*wq, - kthread_should_stop(), - msecs_to_jiffies(wait_ms)); - if (kthread_should_stop()) - break; - - f2fs_balance_fs(sbi); - - if (!test_opt(sbi, BG_GC)) - continue; - - /* - * [GC triggering condition] - * 0. GC is not conducted currently. - * 1. There are enough dirty segments. - * 2. IO subsystem is idle by checking the # of writeback pages. - * 3. IO subsystem is idle by checking the # of requests in - * bdev's request list. - * - * Note) We have to avoid triggering GCs too much frequently. - * Because it is possible that some segments can be - * invalidated soon after by user update or deletion. - * So, I'd like to wait some time to collect dirty segments. - */ - if (!mutex_trylock(&sbi->gc_mutex)) - continue; - - if (!is_idle(sbi)) { - wait_ms = increase_sleep_time(wait_ms); - mutex_unlock(&sbi->gc_mutex); - continue; - } - - if (has_enough_invalid_blocks(sbi)) - wait_ms = decrease_sleep_time(wait_ms); - else - wait_ms = increase_sleep_time(wait_ms); - - sbi->bg_gc++; - - if (f2fs_gc(sbi, 1) == GC_NONE) - wait_ms = GC_THREAD_NOGC_SLEEP_TIME; - else if (wait_ms == GC_THREAD_NOGC_SLEEP_TIME) - wait_ms = GC_THREAD_MAX_SLEEP_TIME; - - } while (!kthread_should_stop()); - return 0; -} - -int start_gc_thread(struct f2fs_sb_info *sbi) -{ - struct f2fs_gc_kthread *gc_th; - - gc_th = kmalloc(sizeof(struct f2fs_gc_kthread), GFP_KERNEL); - if (!gc_th) - return -ENOMEM; - - sbi->gc_thread = gc_th; - init_waitqueue_head(&sbi->gc_thread->gc_wait_queue_head); - sbi->gc_thread->f2fs_gc_task = kthread_run(gc_thread_func, sbi, - GC_THREAD_NAME); - if (IS_ERR(gc_th->f2fs_gc_task)) { - kfree(gc_th); - return -ENOMEM; - } - return 0; -} - -void stop_gc_thread(struct f2fs_sb_info *sbi) -{ - struct f2fs_gc_kthread *gc_th = sbi->gc_thread; - if (!gc_th) - return; - kthread_stop(gc_th->f2fs_gc_task); - kfree(gc_th); - sbi->gc_thread = NULL; -} - -static int select_gc_type(int gc_type) -{ - return (gc_type == BG_GC) ? GC_CB : GC_GREEDY; -} - -static void select_policy(struct f2fs_sb_info *sbi, int gc_type, - int type, struct victim_sel_policy *p) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - - if (p->alloc_mode) { - p->gc_mode = GC_GREEDY; - p->dirty_segmap = dirty_i->dirty_segmap[type]; - p->ofs_unit = 1; - } else { - p->gc_mode = select_gc_type(gc_type); - p->dirty_segmap = dirty_i->dirty_segmap[DIRTY]; - p->ofs_unit = sbi->segs_per_sec; - } - p->offset = sbi->last_victim[p->gc_mode]; -} - -static unsigned int get_max_cost(struct f2fs_sb_info *sbi, - struct victim_sel_policy *p) -{ - if (p->gc_mode == GC_GREEDY) - return (1 << sbi->log_blocks_per_seg) * p->ofs_unit; - else if (p->gc_mode == GC_CB) - return UINT_MAX; - else /* No other gc_mode */ - return 0; -} - -static unsigned int check_bg_victims(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned int segno; - - /* - * If the gc_type is FG_GC, we can select victim segments - * selected by background GC before. - * Those segments guarantee they have small valid blocks. - */ - segno = find_next_bit(dirty_i->victim_segmap[BG_GC], - TOTAL_SEGS(sbi), 0); - if (segno < TOTAL_SEGS(sbi)) { - clear_bit(segno, dirty_i->victim_segmap[BG_GC]); - return segno; - } - return NULL_SEGNO; -} - -static unsigned int get_cb_cost(struct f2fs_sb_info *sbi, unsigned int segno) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned int secno = GET_SECNO(sbi, segno); - unsigned int start = secno * sbi->segs_per_sec; - unsigned long long mtime = 0; - unsigned int vblocks; - unsigned char age = 0; - unsigned char u; - unsigned int i; - - for (i = 0; i < sbi->segs_per_sec; i++) - mtime += get_seg_entry(sbi, start + i)->mtime; - vblocks = get_valid_blocks(sbi, segno, sbi->segs_per_sec); - - mtime = div_u64(mtime, sbi->segs_per_sec); - vblocks = div_u64(vblocks, sbi->segs_per_sec); - - u = (vblocks * 100) >> sbi->log_blocks_per_seg; - - /* Handle if the system time is changed by user */ - if (mtime < sit_i->min_mtime) - sit_i->min_mtime = mtime; - if (mtime > sit_i->max_mtime) - sit_i->max_mtime = mtime; - if (sit_i->max_mtime != sit_i->min_mtime) - age = 100 - div64_u64(100 * (mtime - sit_i->min_mtime), - sit_i->max_mtime - sit_i->min_mtime); - - return UINT_MAX - ((100 * (100 - u) * age) / (100 + u)); -} - -static unsigned int get_gc_cost(struct f2fs_sb_info *sbi, unsigned int segno, - struct victim_sel_policy *p) -{ - if (p->alloc_mode == SSR) - return get_seg_entry(sbi, segno)->ckpt_valid_blocks; - - /* alloc_mode == LFS */ - if (p->gc_mode == GC_GREEDY) - return get_valid_blocks(sbi, segno, sbi->segs_per_sec); - else - return get_cb_cost(sbi, segno); -} - -/* - * This function is called from two pathes. - * One is garbage collection and the other is SSR segment selection. - * When it is called during GC, it just gets a victim segment - * and it does not remove it from dirty seglist. - * When it is called from SSR segment selection, it finds a segment - * which has minimum valid blocks and removes it from dirty seglist. - */ -static int get_victim_by_default(struct f2fs_sb_info *sbi, - unsigned int *result, int gc_type, int type, char alloc_mode) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - struct victim_sel_policy p; - unsigned int segno; - int nsearched = 0; - - p.alloc_mode = alloc_mode; - select_policy(sbi, gc_type, type, &p); - - p.min_segno = NULL_SEGNO; - p.min_cost = get_max_cost(sbi, &p); - - mutex_lock(&dirty_i->seglist_lock); - - if (p.alloc_mode == LFS && gc_type == FG_GC) { - p.min_segno = check_bg_victims(sbi); - if (p.min_segno != NULL_SEGNO) - goto got_it; - } - - while (1) { - unsigned long cost; - - segno = find_next_bit(p.dirty_segmap, - TOTAL_SEGS(sbi), p.offset); - if (segno >= TOTAL_SEGS(sbi)) { - if (sbi->last_victim[p.gc_mode]) { - sbi->last_victim[p.gc_mode] = 0; - p.offset = 0; - continue; - } - break; - } - p.offset = ((segno / p.ofs_unit) * p.ofs_unit) + p.ofs_unit; - - if (test_bit(segno, dirty_i->victim_segmap[FG_GC])) - continue; - if (gc_type == BG_GC && - test_bit(segno, dirty_i->victim_segmap[BG_GC])) - continue; - if (IS_CURSEC(sbi, GET_SECNO(sbi, segno))) - continue; - - cost = get_gc_cost(sbi, segno, &p); - - if (p.min_cost > cost) { - p.min_segno = segno; - p.min_cost = cost; - } - - if (cost == get_max_cost(sbi, &p)) - continue; - - if (nsearched++ >= MAX_VICTIM_SEARCH) { - sbi->last_victim[p.gc_mode] = segno; - break; - } - } -got_it: - if (p.min_segno != NULL_SEGNO) { - *result = (p.min_segno / p.ofs_unit) * p.ofs_unit; - if (p.alloc_mode == LFS) { - int i; - for (i = 0; i < p.ofs_unit; i++) - set_bit(*result + i, - dirty_i->victim_segmap[gc_type]); - } - } - mutex_unlock(&dirty_i->seglist_lock); - - return (p.min_segno == NULL_SEGNO) ? 0 : 1; -} - -static const struct victim_selection default_v_ops = { - .get_victim = get_victim_by_default, -}; - -static struct inode *find_gc_inode(nid_t ino, struct list_head *ilist) -{ - struct list_head *this; - struct inode_entry *ie; - - list_for_each(this, ilist) { - ie = list_entry(this, struct inode_entry, list); - if (ie->inode->i_ino == ino) - return ie->inode; - } - return NULL; -} - -static void add_gc_inode(struct inode *inode, struct list_head *ilist) -{ - struct list_head *this; - struct inode_entry *new_ie, *ie; - - list_for_each(this, ilist) { - ie = list_entry(this, struct inode_entry, list); - if (ie->inode == inode) { - iput(inode); - return; - } - } -repeat: - new_ie = kmem_cache_alloc(winode_slab, GFP_NOFS); - if (!new_ie) { - cond_resched(); - goto repeat; - } - new_ie->inode = inode; - list_add_tail(&new_ie->list, ilist); -} - -static void put_gc_inode(struct list_head *ilist) -{ - struct inode_entry *ie, *next_ie; - list_for_each_entry_safe(ie, next_ie, ilist, list) { - iput(ie->inode); - list_del(&ie->list); - kmem_cache_free(winode_slab, ie); - } -} - -static int check_valid_map(struct f2fs_sb_info *sbi, - unsigned int segno, int offset) -{ - struct sit_info *sit_i = SIT_I(sbi); - struct seg_entry *sentry; - int ret; - - mutex_lock(&sit_i->sentry_lock); - sentry = get_seg_entry(sbi, segno); - ret = f2fs_test_bit(offset, sentry->cur_valid_map); - mutex_unlock(&sit_i->sentry_lock); - return ret ? GC_OK : GC_NEXT; -} - -/* - * This function compares node address got in summary with that in NAT. - * On validity, copy that node with cold status, otherwise (invalid node) - * ignore that. - */ -static int gc_node_segment(struct f2fs_sb_info *sbi, - struct f2fs_summary *sum, unsigned int segno, int gc_type) -{ - bool initial = true; - struct f2fs_summary *entry; - int off; - -next_step: - entry = sum; - for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { - nid_t nid = le32_to_cpu(entry->nid); - struct page *node_page; - int err; - - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - return GC_BLOCKED; - } - - err = check_valid_map(sbi, segno, off); - if (err == GC_ERROR) - return err; - else if (err == GC_NEXT) - continue; - - if (initial) { - ra_node_page(sbi, nid); - continue; - } - node_page = get_node_page(sbi, nid); - if (IS_ERR(node_page)) - continue; - - /* set page dirty and write it */ - if (!PageWriteback(node_page)) - set_page_dirty(node_page); - f2fs_put_page(node_page, 1); - stat_inc_node_blk_count(sbi, 1); - } - if (initial) { - initial = false; - goto next_step; - } - - if (gc_type == FG_GC) { - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - sync_node_pages(sbi, 0, &wbc); - } - return GC_DONE; -} - -/* - * Calculate start block index that this node page contains - */ -block_t start_bidx_of_node(unsigned int node_ofs) -{ - block_t start_bidx; - unsigned int bidx, indirect_blks; - int dec; - - indirect_blks = 2 * NIDS_PER_BLOCK + 4; - - start_bidx = 1; - if (node_ofs == 0) { - start_bidx = 0; - } else if (node_ofs <= 2) { - bidx = node_ofs - 1; - } else if (node_ofs <= indirect_blks) { - dec = (node_ofs - 4) / (NIDS_PER_BLOCK + 1); - bidx = node_ofs - 2 - dec; - } else { - dec = (node_ofs - indirect_blks - 3) / (NIDS_PER_BLOCK + 1); - bidx = node_ofs - 5 - dec; - } - - if (start_bidx) - start_bidx = bidx * ADDRS_PER_BLOCK + ADDRS_PER_INODE; - return start_bidx; -} - -static int check_dnode(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, - struct node_info *dni, block_t blkaddr, unsigned int *nofs) -{ - struct page *node_page; - nid_t nid; - unsigned int ofs_in_node; - block_t source_blkaddr; - - nid = le32_to_cpu(sum->nid); - ofs_in_node = le16_to_cpu(sum->ofs_in_node); - - node_page = get_node_page(sbi, nid); - if (IS_ERR(node_page)) - return GC_NEXT; - - get_node_info(sbi, nid, dni); - - if (sum->version != dni->version) { - f2fs_put_page(node_page, 1); - return GC_NEXT; - } - - *nofs = ofs_of_node(node_page); - source_blkaddr = datablock_addr(node_page, ofs_in_node); - f2fs_put_page(node_page, 1); - - if (source_blkaddr != blkaddr) - return GC_NEXT; - return GC_OK; -} - -static void move_data_page(struct inode *inode, struct page *page, int gc_type) -{ - if (page->mapping != inode->i_mapping) - goto out; - - if (inode != page->mapping->host) - goto out; - - if (PageWriteback(page)) - goto out; - - if (gc_type == BG_GC) { - set_page_dirty(page); - set_cold_data(page); - } else { - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - mutex_lock_op(sbi, DATA_WRITE); - if (clear_page_dirty_for_io(page) && - S_ISDIR(inode->i_mode)) { - dec_page_count(sbi, F2FS_DIRTY_DENTS); - inode_dec_dirty_dents(inode); - } - set_cold_data(page); - do_write_data_page(page); - mutex_unlock_op(sbi, DATA_WRITE); - clear_cold_data(page); - } -out: - f2fs_put_page(page, 1); -} - -/* - * This function tries to get parent node of victim data block, and identifies - * data block validity. If the block is valid, copy that with cold status and - * modify parent node. - * If the parent node is not valid or the data block address is different, - * the victim data block is ignored. - */ -static int gc_data_segment(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, - struct list_head *ilist, unsigned int segno, int gc_type) -{ - struct super_block *sb = sbi->sb; - struct f2fs_summary *entry; - block_t start_addr; - int err, off; - int phase = 0; - - start_addr = START_BLOCK(sbi, segno); - -next_step: - entry = sum; - for (off = 0; off < sbi->blocks_per_seg; off++, entry++) { - struct page *data_page; - struct inode *inode; - struct node_info dni; /* dnode info for the data */ - unsigned int ofs_in_node, nofs; - block_t start_bidx; - - /* - * It makes sure that free segments are able to write - * all the dirty node pages before CP after this CP. - * So let's check the space of dirty node pages. - */ - if (should_do_checkpoint(sbi)) { - mutex_lock(&sbi->cp_mutex); - block_operations(sbi); - err = GC_BLOCKED; - goto stop; - } - - err = check_valid_map(sbi, segno, off); - if (err == GC_ERROR) - goto stop; - else if (err == GC_NEXT) - continue; - - if (phase == 0) { - ra_node_page(sbi, le32_to_cpu(entry->nid)); - continue; - } - - /* Get an inode by ino with checking validity */ - err = check_dnode(sbi, entry, &dni, start_addr + off, &nofs); - if (err == GC_ERROR) - goto stop; - else if (err == GC_NEXT) - continue; - - if (phase == 1) { - ra_node_page(sbi, dni.ino); - continue; - } - - start_bidx = start_bidx_of_node(nofs); - ofs_in_node = le16_to_cpu(entry->ofs_in_node); - - if (phase == 2) { - inode = f2fs_iget_nowait(sb, dni.ino); - if (IS_ERR(inode)) - continue; - - data_page = find_data_page(inode, - start_bidx + ofs_in_node); - if (IS_ERR(data_page)) - goto next_iput; - - f2fs_put_page(data_page, 0); - add_gc_inode(inode, ilist); - } else { - inode = find_gc_inode(dni.ino, ilist); - if (inode) { - data_page = get_lock_data_page(inode, - start_bidx + ofs_in_node); - if (IS_ERR(data_page)) - continue; - move_data_page(inode, data_page, gc_type); - stat_inc_data_blk_count(sbi, 1); - } - } - continue; -next_iput: - iput(inode); - } - if (++phase < 4) - goto next_step; - err = GC_DONE; -stop: - if (gc_type == FG_GC) - f2fs_submit_bio(sbi, DATA, true); - return err; -} - -static int __get_victim(struct f2fs_sb_info *sbi, unsigned int *victim, - int gc_type, int type) -{ - struct sit_info *sit_i = SIT_I(sbi); - int ret; - mutex_lock(&sit_i->sentry_lock); - ret = DIRTY_I(sbi)->v_ops->get_victim(sbi, victim, gc_type, type, LFS); - mutex_unlock(&sit_i->sentry_lock); - return ret; -} - -static int do_garbage_collect(struct f2fs_sb_info *sbi, unsigned int segno, - struct list_head *ilist, int gc_type) -{ - struct page *sum_page; - struct f2fs_summary_block *sum; - int ret = GC_DONE; - - /* read segment summary of victim */ - sum_page = get_sum_page(sbi, segno); - if (IS_ERR(sum_page)) - return GC_ERROR; - - /* - * CP needs to lock sum_page. In this time, we don't need - * to lock this page, because this summary page is not gone anywhere. - * Also, this page is not gonna be updated before GC is done. - */ - unlock_page(sum_page); - sum = page_address(sum_page); - - switch (GET_SUM_TYPE((&sum->footer))) { - case SUM_TYPE_NODE: - ret = gc_node_segment(sbi, sum->entries, segno, gc_type); - break; - case SUM_TYPE_DATA: - ret = gc_data_segment(sbi, sum->entries, ilist, segno, gc_type); - break; - } - stat_inc_seg_count(sbi, GET_SUM_TYPE((&sum->footer))); - stat_inc_call_count(sbi->stat_info); - - f2fs_put_page(sum_page, 0); - return ret; -} - -int f2fs_gc(struct f2fs_sb_info *sbi, int nGC) -{ - unsigned int segno; - int old_free_secs, cur_free_secs; - int gc_status, nfree; - struct list_head ilist; - int gc_type = BG_GC; - - INIT_LIST_HEAD(&ilist); -gc_more: - nfree = 0; - gc_status = GC_NONE; - - if (has_not_enough_free_secs(sbi)) - old_free_secs = reserved_sections(sbi); - else - old_free_secs = free_sections(sbi); - - while (sbi->sb->s_flags & MS_ACTIVE) { - int i; - if (has_not_enough_free_secs(sbi)) - gc_type = FG_GC; - - cur_free_secs = free_sections(sbi) + nfree; - - /* We got free space successfully. */ - if (nGC < cur_free_secs - old_free_secs) - break; - - if (!__get_victim(sbi, &segno, gc_type, NO_CHECK_TYPE)) - break; - - for (i = 0; i < sbi->segs_per_sec; i++) { - /* - * do_garbage_collect will give us three gc_status: - * GC_ERROR, GC_DONE, and GC_BLOCKED. - * If GC is finished uncleanly, we have to return - * the victim to dirty segment list. - */ - gc_status = do_garbage_collect(sbi, segno + i, - &ilist, gc_type); - if (gc_status != GC_DONE) - goto stop; - nfree++; - } - } -stop: - if (has_not_enough_free_secs(sbi) || gc_status == GC_BLOCKED) { - write_checkpoint(sbi, (gc_status == GC_BLOCKED), false); - if (nfree) - goto gc_more; - } - mutex_unlock(&sbi->gc_mutex); - - put_gc_inode(&ilist); - BUG_ON(!list_empty(&ilist)); - return gc_status; -} - -void build_gc_manager(struct f2fs_sb_info *sbi) -{ - DIRTY_I(sbi)->v_ops = &default_v_ops; -} - -int create_gc_caches(void) -{ - winode_slab = f2fs_kmem_cache_create("f2fs_gc_inodes", - sizeof(struct inode_entry), NULL); - if (!winode_slab) - return -ENOMEM; - return 0; -} - -void destroy_gc_caches(void) -{ - kmem_cache_destroy(winode_slab); -} diff --git a/trunk/fs/f2fs/gc.h b/trunk/fs/f2fs/gc.h deleted file mode 100644 index b026d9354ccd..000000000000 --- a/trunk/fs/f2fs/gc.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * fs/f2fs/gc.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#define GC_THREAD_NAME "f2fs_gc_task" -#define GC_THREAD_MIN_WB_PAGES 1 /* - * a threshold to determine - * whether IO subsystem is idle - * or not - */ -#define GC_THREAD_MIN_SLEEP_TIME 10000 /* milliseconds */ -#define GC_THREAD_MAX_SLEEP_TIME 30000 -#define GC_THREAD_NOGC_SLEEP_TIME 10000 -#define LIMIT_INVALID_BLOCK 40 /* percentage over total user space */ -#define LIMIT_FREE_BLOCK 40 /* percentage over invalid + free space */ - -/* Search max. number of dirty segments to select a victim segment */ -#define MAX_VICTIM_SEARCH 20 - -enum { - GC_NONE = 0, - GC_ERROR, - GC_OK, - GC_NEXT, - GC_BLOCKED, - GC_DONE, -}; - -struct f2fs_gc_kthread { - struct task_struct *f2fs_gc_task; - wait_queue_head_t gc_wait_queue_head; -}; - -struct inode_entry { - struct list_head list; - struct inode *inode; -}; - -/* - * inline functions - */ -static inline block_t free_user_blocks(struct f2fs_sb_info *sbi) -{ - if (free_segments(sbi) < overprovision_segments(sbi)) - return 0; - else - return (free_segments(sbi) - overprovision_segments(sbi)) - << sbi->log_blocks_per_seg; -} - -static inline block_t limit_invalid_user_blocks(struct f2fs_sb_info *sbi) -{ - return (long)(sbi->user_block_count * LIMIT_INVALID_BLOCK) / 100; -} - -static inline block_t limit_free_user_blocks(struct f2fs_sb_info *sbi) -{ - block_t reclaimable_user_blocks = sbi->user_block_count - - written_block_count(sbi); - return (long)(reclaimable_user_blocks * LIMIT_FREE_BLOCK) / 100; -} - -static inline long increase_sleep_time(long wait) -{ - wait += GC_THREAD_MIN_SLEEP_TIME; - if (wait > GC_THREAD_MAX_SLEEP_TIME) - wait = GC_THREAD_MAX_SLEEP_TIME; - return wait; -} - -static inline long decrease_sleep_time(long wait) -{ - wait -= GC_THREAD_MIN_SLEEP_TIME; - if (wait <= GC_THREAD_MIN_SLEEP_TIME) - wait = GC_THREAD_MIN_SLEEP_TIME; - return wait; -} - -static inline bool has_enough_invalid_blocks(struct f2fs_sb_info *sbi) -{ - block_t invalid_user_blocks = sbi->user_block_count - - written_block_count(sbi); - /* - * Background GC is triggered with the following condition. - * 1. There are a number of invalid blocks. - * 2. There is not enough free space. - */ - if (invalid_user_blocks > limit_invalid_user_blocks(sbi) && - free_user_blocks(sbi) < limit_free_user_blocks(sbi)) - return true; - return false; -} - -static inline int is_idle(struct f2fs_sb_info *sbi) -{ - struct block_device *bdev = sbi->sb->s_bdev; - struct request_queue *q = bdev_get_queue(bdev); - struct request_list *rl = &q->root_rl; - return !(rl->count[BLK_RW_SYNC]) && !(rl->count[BLK_RW_ASYNC]); -} - -static inline bool should_do_checkpoint(struct f2fs_sb_info *sbi) -{ - unsigned int pages_per_sec = sbi->segs_per_sec * - (1 << sbi->log_blocks_per_seg); - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - return free_sections(sbi) <= (node_secs + 2 * dent_secs + 2); -} diff --git a/trunk/fs/f2fs/hash.c b/trunk/fs/f2fs/hash.c deleted file mode 100644 index a60f04200f8b..000000000000 --- a/trunk/fs/f2fs/hash.c +++ /dev/null @@ -1,97 +0,0 @@ -/* - * fs/f2fs/hash.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Portions of this code from linux/fs/ext3/hash.c - * - * Copyright (C) 2002 by Theodore Ts'o - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include - -#include "f2fs.h" - -/* - * Hashing code copied from ext3 - */ -#define DELTA 0x9E3779B9 - -static void TEA_transform(unsigned int buf[4], unsigned int const in[]) -{ - __u32 sum = 0; - __u32 b0 = buf[0], b1 = buf[1]; - __u32 a = in[0], b = in[1], c = in[2], d = in[3]; - int n = 16; - - do { - sum += DELTA; - b0 += ((b1 << 4)+a) ^ (b1+sum) ^ ((b1 >> 5)+b); - b1 += ((b0 << 4)+c) ^ (b0+sum) ^ ((b0 >> 5)+d); - } while (--n); - - buf[0] += b0; - buf[1] += b1; -} - -static void str2hashbuf(const char *msg, int len, unsigned int *buf, int num) -{ - unsigned pad, val; - int i; - - pad = (__u32)len | ((__u32)len << 8); - pad |= pad << 16; - - val = pad; - if (len > num * 4) - len = num * 4; - for (i = 0; i < len; i++) { - if ((i % 4) == 0) - val = pad; - val = msg[i] + (val << 8); - if ((i % 4) == 3) { - *buf++ = val; - val = pad; - num--; - } - } - if (--num >= 0) - *buf++ = val; - while (--num >= 0) - *buf++ = pad; -} - -f2fs_hash_t f2fs_dentry_hash(const char *name, int len) -{ - __u32 hash, minor_hash; - f2fs_hash_t f2fs_hash; - const char *p; - __u32 in[8], buf[4]; - - /* Initialize the default seed for the hash checksum functions */ - buf[0] = 0x67452301; - buf[1] = 0xefcdab89; - buf[2] = 0x98badcfe; - buf[3] = 0x10325476; - - p = name; - while (len > 0) { - str2hashbuf(p, len, in, 4); - TEA_transform(buf, in); - len -= 16; - p += 16; - } - hash = buf[0]; - minor_hash = buf[1]; - - f2fs_hash = cpu_to_le32(hash & ~F2FS_HASH_COL_BIT); - return f2fs_hash; -} diff --git a/trunk/fs/f2fs/inode.c b/trunk/fs/f2fs/inode.c deleted file mode 100644 index df5fb381ebf1..000000000000 --- a/trunk/fs/f2fs/inode.c +++ /dev/null @@ -1,268 +0,0 @@ -/* - * fs/f2fs/inode.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" - -struct f2fs_iget_args { - u64 ino; - int on_free; -}; - -void f2fs_set_inode_flags(struct inode *inode) -{ - unsigned int flags = F2FS_I(inode)->i_flags; - - inode->i_flags &= ~(S_SYNC | S_APPEND | S_IMMUTABLE | - S_NOATIME | S_DIRSYNC); - - if (flags & FS_SYNC_FL) - inode->i_flags |= S_SYNC; - if (flags & FS_APPEND_FL) - inode->i_flags |= S_APPEND; - if (flags & FS_IMMUTABLE_FL) - inode->i_flags |= S_IMMUTABLE; - if (flags & FS_NOATIME_FL) - inode->i_flags |= S_NOATIME; - if (flags & FS_DIRSYNC_FL) - inode->i_flags |= S_DIRSYNC; -} - -static int f2fs_iget_test(struct inode *inode, void *data) -{ - struct f2fs_iget_args *args = data; - - if (inode->i_ino != args->ino) - return 0; - if (inode->i_state & (I_FREEING | I_WILL_FREE)) { - args->on_free = 1; - return 0; - } - return 1; -} - -struct inode *f2fs_iget_nowait(struct super_block *sb, unsigned long ino) -{ - struct f2fs_iget_args args = { - .ino = ino, - .on_free = 0 - }; - struct inode *inode = ilookup5(sb, ino, f2fs_iget_test, &args); - - if (inode) - return inode; - if (!args.on_free) - return f2fs_iget(sb, ino); - return ERR_PTR(-ENOENT); -} - -static int do_read_inode(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct page *node_page; - struct f2fs_node *rn; - struct f2fs_inode *ri; - - /* Check if ino is within scope */ - check_nid_range(sbi, inode->i_ino); - - node_page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(node_page)) - return PTR_ERR(node_page); - - rn = page_address(node_page); - ri = &(rn->i); - - inode->i_mode = le16_to_cpu(ri->i_mode); - i_uid_write(inode, le32_to_cpu(ri->i_uid)); - i_gid_write(inode, le32_to_cpu(ri->i_gid)); - set_nlink(inode, le32_to_cpu(ri->i_links)); - inode->i_size = le64_to_cpu(ri->i_size); - inode->i_blocks = le64_to_cpu(ri->i_blocks); - - inode->i_atime.tv_sec = le64_to_cpu(ri->i_atime); - inode->i_ctime.tv_sec = le64_to_cpu(ri->i_ctime); - inode->i_mtime.tv_sec = le64_to_cpu(ri->i_mtime); - inode->i_atime.tv_nsec = le32_to_cpu(ri->i_atime_nsec); - inode->i_ctime.tv_nsec = le32_to_cpu(ri->i_ctime_nsec); - inode->i_mtime.tv_nsec = le32_to_cpu(ri->i_mtime_nsec); - inode->i_generation = le32_to_cpu(ri->i_generation); - - fi->i_current_depth = le32_to_cpu(ri->i_current_depth); - fi->i_xattr_nid = le32_to_cpu(ri->i_xattr_nid); - fi->i_flags = le32_to_cpu(ri->i_flags); - fi->flags = 0; - fi->data_version = le64_to_cpu(F2FS_CKPT(sbi)->checkpoint_ver) - 1; - fi->i_advise = ri->i_advise; - fi->i_pino = le32_to_cpu(ri->i_pino); - get_extent_info(&fi->ext, ri->i_ext); - f2fs_put_page(node_page, 1); - return 0; -} - -struct inode *f2fs_iget(struct super_block *sb, unsigned long ino) -{ - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode; - int ret; - - inode = iget_locked(sb, ino); - if (!inode) - return ERR_PTR(-ENOMEM); - if (!(inode->i_state & I_NEW)) - return inode; - if (ino == F2FS_NODE_INO(sbi) || ino == F2FS_META_INO(sbi)) - goto make_now; - - ret = do_read_inode(inode); - if (ret) - goto bad_inode; - - if (!sbi->por_doing && inode->i_nlink == 0) { - ret = -ENOENT; - goto bad_inode; - } - -make_now: - if (ino == F2FS_NODE_INO(sbi)) { - inode->i_mapping->a_ops = &f2fs_node_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); - } else if (ino == F2FS_META_INO(sbi)) { - inode->i_mapping->a_ops = &f2fs_meta_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); - } else if (S_ISREG(inode->i_mode)) { - inode->i_op = &f2fs_file_inode_operations; - inode->i_fop = &f2fs_file_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - } else if (S_ISDIR(inode->i_mode)) { - inode->i_op = &f2fs_dir_inode_operations; - inode->i_fop = &f2fs_dir_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_HIGHUSER_MOVABLE | - __GFP_ZERO); - } else if (S_ISLNK(inode->i_mode)) { - inode->i_op = &f2fs_symlink_inode_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - } else if (S_ISCHR(inode->i_mode) || S_ISBLK(inode->i_mode) || - S_ISFIFO(inode->i_mode) || S_ISSOCK(inode->i_mode)) { - inode->i_op = &f2fs_special_inode_operations; - init_special_inode(inode, inode->i_mode, inode->i_rdev); - } else { - ret = -EIO; - goto bad_inode; - } - unlock_new_inode(inode); - - return inode; - -bad_inode: - iget_failed(inode); - return ERR_PTR(ret); -} - -void update_inode(struct inode *inode, struct page *node_page) -{ - struct f2fs_node *rn; - struct f2fs_inode *ri; - - wait_on_page_writeback(node_page); - - rn = page_address(node_page); - ri = &(rn->i); - - ri->i_mode = cpu_to_le16(inode->i_mode); - ri->i_advise = F2FS_I(inode)->i_advise; - ri->i_uid = cpu_to_le32(i_uid_read(inode)); - ri->i_gid = cpu_to_le32(i_gid_read(inode)); - ri->i_links = cpu_to_le32(inode->i_nlink); - ri->i_size = cpu_to_le64(i_size_read(inode)); - ri->i_blocks = cpu_to_le64(inode->i_blocks); - set_raw_extent(&F2FS_I(inode)->ext, &ri->i_ext); - - ri->i_atime = cpu_to_le64(inode->i_atime.tv_sec); - ri->i_ctime = cpu_to_le64(inode->i_ctime.tv_sec); - ri->i_mtime = cpu_to_le64(inode->i_mtime.tv_sec); - ri->i_atime_nsec = cpu_to_le32(inode->i_atime.tv_nsec); - ri->i_ctime_nsec = cpu_to_le32(inode->i_ctime.tv_nsec); - ri->i_mtime_nsec = cpu_to_le32(inode->i_mtime.tv_nsec); - ri->i_current_depth = cpu_to_le32(F2FS_I(inode)->i_current_depth); - ri->i_xattr_nid = cpu_to_le32(F2FS_I(inode)->i_xattr_nid); - ri->i_flags = cpu_to_le32(F2FS_I(inode)->i_flags); - ri->i_pino = cpu_to_le32(F2FS_I(inode)->i_pino); - ri->i_generation = cpu_to_le32(inode->i_generation); - set_page_dirty(node_page); -} - -int f2fs_write_inode(struct inode *inode, struct writeback_control *wbc) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct page *node_page; - bool need_lock = false; - - if (inode->i_ino == F2FS_NODE_INO(sbi) || - inode->i_ino == F2FS_META_INO(sbi)) - return 0; - - node_page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(node_page)) - return PTR_ERR(node_page); - - if (!PageDirty(node_page)) { - need_lock = true; - f2fs_put_page(node_page, 1); - mutex_lock(&sbi->write_inode); - node_page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(node_page)) { - mutex_unlock(&sbi->write_inode); - return PTR_ERR(node_page); - } - } - update_inode(inode, node_page); - f2fs_put_page(node_page, 1); - if (need_lock) - mutex_unlock(&sbi->write_inode); - return 0; -} - -/* - * Called at the last iput() if i_nlink is zero - */ -void f2fs_evict_inode(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - - truncate_inode_pages(&inode->i_data, 0); - - if (inode->i_ino == F2FS_NODE_INO(sbi) || - inode->i_ino == F2FS_META_INO(sbi)) - goto no_delete; - - BUG_ON(atomic_read(&F2FS_I(inode)->dirty_dents)); - remove_dirty_dir_inode(inode); - - if (inode->i_nlink || is_bad_inode(inode)) - goto no_delete; - - set_inode_flag(F2FS_I(inode), FI_NO_ALLOC); - i_size_write(inode, 0); - - if (F2FS_HAS_BLOCKS(inode)) - f2fs_truncate(inode); - - remove_inode_page(inode); -no_delete: - clear_inode(inode); -} diff --git a/trunk/fs/f2fs/namei.c b/trunk/fs/f2fs/namei.c deleted file mode 100644 index 89b7675dc377..000000000000 --- a/trunk/fs/f2fs/namei.c +++ /dev/null @@ -1,503 +0,0 @@ -/* - * fs/f2fs/namei.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "xattr.h" -#include "acl.h" - -static struct inode *f2fs_new_inode(struct inode *dir, umode_t mode) -{ - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - nid_t ino; - struct inode *inode; - bool nid_free = false; - int err; - - inode = new_inode(sb); - if (!inode) - return ERR_PTR(-ENOMEM); - - mutex_lock_op(sbi, NODE_NEW); - if (!alloc_nid(sbi, &ino)) { - mutex_unlock_op(sbi, NODE_NEW); - err = -ENOSPC; - goto fail; - } - mutex_unlock_op(sbi, NODE_NEW); - - inode->i_uid = current_fsuid(); - - if (dir->i_mode & S_ISGID) { - inode->i_gid = dir->i_gid; - if (S_ISDIR(mode)) - mode |= S_ISGID; - } else { - inode->i_gid = current_fsgid(); - } - - inode->i_ino = ino; - inode->i_mode = mode; - inode->i_blocks = 0; - inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME; - inode->i_generation = sbi->s_next_generation++; - - err = insert_inode_locked(inode); - if (err) { - err = -EINVAL; - nid_free = true; - goto out; - } - - mark_inode_dirty(inode); - return inode; - -out: - clear_nlink(inode); - unlock_new_inode(inode); -fail: - iput(inode); - if (nid_free) - alloc_nid_failed(sbi, ino); - return ERR_PTR(err); -} - -static int is_multimedia_file(const unsigned char *s, const char *sub) -{ - int slen = strlen(s); - int sublen = strlen(sub); - int ret; - - if (sublen > slen) - return 1; - - ret = memcmp(s + slen - sublen, sub, sublen); - if (ret) { /* compare upper case */ - int i; - char upper_sub[8]; - for (i = 0; i < sublen && i < sizeof(upper_sub); i++) - upper_sub[i] = toupper(sub[i]); - return memcmp(s + slen - sublen, upper_sub, sublen); - } - - return ret; -} - -/* - * Set multimedia files as cold files for hot/cold data separation - */ -static inline void set_cold_file(struct f2fs_sb_info *sbi, struct inode *inode, - const unsigned char *name) -{ - int i; - __u8 (*extlist)[8] = sbi->raw_super->extension_list; - - int count = le32_to_cpu(sbi->raw_super->extension_count); - for (i = 0; i < count; i++) { - if (!is_multimedia_file(name, extlist[i])) { - F2FS_I(inode)->i_advise |= FADVISE_COLD_BIT; - break; - } - } -} - -static int f2fs_create(struct inode *dir, struct dentry *dentry, umode_t mode, - bool excl) -{ - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode; - nid_t ino = 0; - int err; - - inode = f2fs_new_inode(dir, mode); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - if (!test_opt(sbi, DISABLE_EXT_IDENTIFY)) - set_cold_file(sbi, inode, dentry->d_name.name); - - inode->i_op = &f2fs_file_inode_operations; - inode->i_fop = &f2fs_file_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - ino = inode->i_ino; - - err = f2fs_add_link(dentry, inode); - if (err) - goto out; - - alloc_nid_done(sbi, ino); - - if (!sbi->por_doing) - d_instantiate(dentry, inode); - unlock_new_inode(inode); - - f2fs_balance_fs(sbi); - return 0; -out: - clear_nlink(inode); - unlock_new_inode(inode); - iput(inode); - alloc_nid_failed(sbi, ino); - return err; -} - -static int f2fs_link(struct dentry *old_dentry, struct inode *dir, - struct dentry *dentry) -{ - struct inode *inode = old_dentry->d_inode; - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - int err; - - inode->i_ctime = CURRENT_TIME; - atomic_inc(&inode->i_count); - - set_inode_flag(F2FS_I(inode), FI_INC_LINK); - err = f2fs_add_link(dentry, inode); - if (err) - goto out; - - d_instantiate(dentry, inode); - - f2fs_balance_fs(sbi); - return 0; -out: - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); - iput(inode); - return err; -} - -struct dentry *f2fs_get_parent(struct dentry *child) -{ - struct qstr dotdot = QSTR_INIT("..", 2); - unsigned long ino = f2fs_inode_by_name(child->d_inode, &dotdot); - if (!ino) - return ERR_PTR(-ENOENT); - return d_obtain_alias(f2fs_iget(child->d_inode->i_sb, ino)); -} - -static struct dentry *f2fs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct inode *inode = NULL; - struct f2fs_dir_entry *de; - struct page *page; - - if (dentry->d_name.len > F2FS_MAX_NAME_LEN) - return ERR_PTR(-ENAMETOOLONG); - - de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (de) { - nid_t ino = le32_to_cpu(de->ino); - kunmap(page); - f2fs_put_page(page, 0); - - inode = f2fs_iget(dir->i_sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - } - - return d_splice_alias(inode, dentry); -} - -static int f2fs_unlink(struct inode *dir, struct dentry *dentry) -{ - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode = dentry->d_inode; - struct f2fs_dir_entry *de; - struct page *page; - int err = -ENOENT; - - de = f2fs_find_entry(dir, &dentry->d_name, &page); - if (!de) - goto fail; - - err = check_orphan_space(sbi); - if (err) { - kunmap(page); - f2fs_put_page(page, 0); - goto fail; - } - - f2fs_delete_entry(de, page, inode); - - /* In order to evict this inode, we set it dirty */ - mark_inode_dirty(inode); - f2fs_balance_fs(sbi); -fail: - return err; -} - -static int f2fs_symlink(struct inode *dir, struct dentry *dentry, - const char *symname) -{ - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode; - unsigned symlen = strlen(symname) + 1; - int err; - - inode = f2fs_new_inode(dir, S_IFLNK | S_IRWXUGO); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - inode->i_op = &f2fs_symlink_inode_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - - err = f2fs_add_link(dentry, inode); - if (err) - goto out; - - err = page_symlink(inode, symname, symlen); - alloc_nid_done(sbi, inode->i_ino); - - d_instantiate(dentry, inode); - unlock_new_inode(inode); - - f2fs_balance_fs(sbi); - - return err; -out: - clear_nlink(inode); - unlock_new_inode(inode); - iput(inode); - alloc_nid_failed(sbi, inode->i_ino); - return err; -} - -static int f2fs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dir->i_sb); - struct inode *inode; - int err; - - inode = f2fs_new_inode(dir, S_IFDIR | mode); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - inode->i_op = &f2fs_dir_inode_operations; - inode->i_fop = &f2fs_dir_operations; - inode->i_mapping->a_ops = &f2fs_dblock_aops; - mapping_set_gfp_mask(inode->i_mapping, GFP_F2FS_ZERO); - - set_inode_flag(F2FS_I(inode), FI_INC_LINK); - err = f2fs_add_link(dentry, inode); - if (err) - goto out_fail; - - alloc_nid_done(sbi, inode->i_ino); - - d_instantiate(dentry, inode); - unlock_new_inode(inode); - - f2fs_balance_fs(sbi); - return 0; - -out_fail: - clear_inode_flag(F2FS_I(inode), FI_INC_LINK); - clear_nlink(inode); - unlock_new_inode(inode); - iput(inode); - alloc_nid_failed(sbi, inode->i_ino); - return err; -} - -static int f2fs_rmdir(struct inode *dir, struct dentry *dentry) -{ - struct inode *inode = dentry->d_inode; - if (f2fs_empty_dir(inode)) - return f2fs_unlink(dir, dentry); - return -ENOTEMPTY; -} - -static int f2fs_mknod(struct inode *dir, struct dentry *dentry, - umode_t mode, dev_t rdev) -{ - struct super_block *sb = dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode; - int err = 0; - - if (!new_valid_dev(rdev)) - return -EINVAL; - - inode = f2fs_new_inode(dir, mode); - if (IS_ERR(inode)) - return PTR_ERR(inode); - - init_special_inode(inode, inode->i_mode, rdev); - inode->i_op = &f2fs_special_inode_operations; - - err = f2fs_add_link(dentry, inode); - if (err) - goto out; - - alloc_nid_done(sbi, inode->i_ino); - d_instantiate(dentry, inode); - unlock_new_inode(inode); - - f2fs_balance_fs(sbi); - - return 0; -out: - clear_nlink(inode); - unlock_new_inode(inode); - iput(inode); - alloc_nid_failed(sbi, inode->i_ino); - return err; -} - -static int f2fs_rename(struct inode *old_dir, struct dentry *old_dentry, - struct inode *new_dir, struct dentry *new_dentry) -{ - struct super_block *sb = old_dir->i_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *old_inode = old_dentry->d_inode; - struct inode *new_inode = new_dentry->d_inode; - struct page *old_dir_page; - struct page *old_page; - struct f2fs_dir_entry *old_dir_entry = NULL; - struct f2fs_dir_entry *old_entry; - struct f2fs_dir_entry *new_entry; - int err = -ENOENT; - - old_entry = f2fs_find_entry(old_dir, &old_dentry->d_name, &old_page); - if (!old_entry) - goto out; - - if (S_ISDIR(old_inode->i_mode)) { - err = -EIO; - old_dir_entry = f2fs_parent_dir(old_inode, &old_dir_page); - if (!old_dir_entry) - goto out_old; - } - - mutex_lock_op(sbi, RENAME); - - if (new_inode) { - struct page *new_page; - - err = -ENOTEMPTY; - if (old_dir_entry && !f2fs_empty_dir(new_inode)) - goto out_dir; - - err = -ENOENT; - new_entry = f2fs_find_entry(new_dir, &new_dentry->d_name, - &new_page); - if (!new_entry) - goto out_dir; - - f2fs_set_link(new_dir, new_entry, new_page, old_inode); - - new_inode->i_ctime = CURRENT_TIME; - if (old_dir_entry) - drop_nlink(new_inode); - drop_nlink(new_inode); - if (!new_inode->i_nlink) - add_orphan_inode(sbi, new_inode->i_ino); - f2fs_write_inode(new_inode, NULL); - } else { - err = f2fs_add_link(new_dentry, old_inode); - if (err) - goto out_dir; - - if (old_dir_entry) { - inc_nlink(new_dir); - f2fs_write_inode(new_dir, NULL); - } - } - - old_inode->i_ctime = CURRENT_TIME; - set_inode_flag(F2FS_I(old_inode), FI_NEED_CP); - mark_inode_dirty(old_inode); - - f2fs_delete_entry(old_entry, old_page, NULL); - - if (old_dir_entry) { - if (old_dir != new_dir) { - f2fs_set_link(old_inode, old_dir_entry, - old_dir_page, new_dir); - } else { - kunmap(old_dir_page); - f2fs_put_page(old_dir_page, 0); - } - drop_nlink(old_dir); - f2fs_write_inode(old_dir, NULL); - } - - mutex_unlock_op(sbi, RENAME); - - f2fs_balance_fs(sbi); - return 0; - -out_dir: - if (old_dir_entry) { - kunmap(old_dir_page); - f2fs_put_page(old_dir_page, 0); - } - mutex_unlock_op(sbi, RENAME); -out_old: - kunmap(old_page); - f2fs_put_page(old_page, 0); -out: - return err; -} - -const struct inode_operations f2fs_dir_inode_operations = { - .create = f2fs_create, - .lookup = f2fs_lookup, - .link = f2fs_link, - .unlink = f2fs_unlink, - .symlink = f2fs_symlink, - .mkdir = f2fs_mkdir, - .rmdir = f2fs_rmdir, - .mknod = f2fs_mknod, - .rename = f2fs_rename, - .setattr = f2fs_setattr, - .get_acl = f2fs_get_acl, -#ifdef CONFIG_F2FS_FS_XATTR - .setxattr = generic_setxattr, - .getxattr = generic_getxattr, - .listxattr = f2fs_listxattr, - .removexattr = generic_removexattr, -#endif -}; - -const struct inode_operations f2fs_symlink_inode_operations = { - .readlink = generic_readlink, - .follow_link = page_follow_link_light, - .put_link = page_put_link, - .setattr = f2fs_setattr, -#ifdef CONFIG_F2FS_FS_XATTR - .setxattr = generic_setxattr, - .getxattr = generic_getxattr, - .listxattr = f2fs_listxattr, - .removexattr = generic_removexattr, -#endif -}; - -const struct inode_operations f2fs_special_inode_operations = { - .setattr = f2fs_setattr, - .get_acl = f2fs_get_acl, -#ifdef CONFIG_F2FS_FS_XATTR - .setxattr = generic_setxattr, - .getxattr = generic_getxattr, - .listxattr = f2fs_listxattr, - .removexattr = generic_removexattr, -#endif -}; diff --git a/trunk/fs/f2fs/node.c b/trunk/fs/f2fs/node.c deleted file mode 100644 index 19870361497e..000000000000 --- a/trunk/fs/f2fs/node.c +++ /dev/null @@ -1,1764 +0,0 @@ -/* - * fs/f2fs/node.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "segment.h" - -static struct kmem_cache *nat_entry_slab; -static struct kmem_cache *free_nid_slab; - -static void clear_node_page_dirty(struct page *page) -{ - struct address_space *mapping = page->mapping; - struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); - unsigned int long flags; - - if (PageDirty(page)) { - spin_lock_irqsave(&mapping->tree_lock, flags); - radix_tree_tag_clear(&mapping->page_tree, - page_index(page), - PAGECACHE_TAG_DIRTY); - spin_unlock_irqrestore(&mapping->tree_lock, flags); - - clear_page_dirty_for_io(page); - dec_page_count(sbi, F2FS_DIRTY_NODES); - } - ClearPageUptodate(page); -} - -static struct page *get_current_nat_page(struct f2fs_sb_info *sbi, nid_t nid) -{ - pgoff_t index = current_nat_addr(sbi, nid); - return get_meta_page(sbi, index); -} - -static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid) -{ - struct page *src_page; - struct page *dst_page; - pgoff_t src_off; - pgoff_t dst_off; - void *src_addr; - void *dst_addr; - struct f2fs_nm_info *nm_i = NM_I(sbi); - - src_off = current_nat_addr(sbi, nid); - dst_off = next_nat_addr(sbi, src_off); - - /* get current nat block page with lock */ - src_page = get_meta_page(sbi, src_off); - - /* Dirty src_page means that it is already the new target NAT page. */ - if (PageDirty(src_page)) - return src_page; - - dst_page = grab_meta_page(sbi, dst_off); - - src_addr = page_address(src_page); - dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); - set_page_dirty(dst_page); - f2fs_put_page(src_page, 1); - - set_to_next_nat(nm_i, nid); - - return dst_page; -} - -/* - * Readahead NAT pages - */ -static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) -{ - struct address_space *mapping = sbi->meta_inode->i_mapping; - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct page *page; - pgoff_t index; - int i; - - for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) { - if (nid >= nm_i->max_nid) - nid = 0; - index = current_nat_addr(sbi, nid); - - page = grab_cache_page(mapping, index); - if (!page) - continue; - if (f2fs_readpage(sbi, page, index, READ)) { - f2fs_put_page(page, 1); - continue; - } - page_cache_release(page); - } -} - -static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n) -{ - return radix_tree_lookup(&nm_i->nat_root, n); -} - -static unsigned int __gang_lookup_nat_cache(struct f2fs_nm_info *nm_i, - nid_t start, unsigned int nr, struct nat_entry **ep) -{ - return radix_tree_gang_lookup(&nm_i->nat_root, (void **)ep, start, nr); -} - -static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e) -{ - list_del(&e->list); - radix_tree_delete(&nm_i->nat_root, nat_get_nid(e)); - nm_i->nat_cnt--; - kmem_cache_free(nat_entry_slab, e); -} - -int is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct nat_entry *e; - int is_cp = 1; - - read_lock(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, nid); - if (e && !e->checkpointed) - is_cp = 0; - read_unlock(&nm_i->nat_tree_lock); - return is_cp; -} - -static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid) -{ - struct nat_entry *new; - - new = kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC); - if (!new) - return NULL; - if (radix_tree_insert(&nm_i->nat_root, nid, new)) { - kmem_cache_free(nat_entry_slab, new); - return NULL; - } - memset(new, 0, sizeof(struct nat_entry)); - nat_set_nid(new, nid); - list_add_tail(&new->list, &nm_i->nat_entries); - nm_i->nat_cnt++; - return new; -} - -static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid, - struct f2fs_nat_entry *ne) -{ - struct nat_entry *e; -retry: - write_lock(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, nid); - if (!e) { - e = grab_nat_entry(nm_i, nid); - if (!e) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; - } - nat_set_blkaddr(e, le32_to_cpu(ne->block_addr)); - nat_set_ino(e, le32_to_cpu(ne->ino)); - nat_set_version(e, ne->version); - e->checkpointed = true; - } - write_unlock(&nm_i->nat_tree_lock); -} - -static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni, - block_t new_blkaddr) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct nat_entry *e; -retry: - write_lock(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, ni->nid); - if (!e) { - e = grab_nat_entry(nm_i, ni->nid); - if (!e) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; - } - e->ni = *ni; - e->checkpointed = true; - BUG_ON(ni->blk_addr == NEW_ADDR); - } else if (new_blkaddr == NEW_ADDR) { - /* - * when nid is reallocated, - * previous nat entry can be remained in nat cache. - * So, reinitialize it with new information. - */ - e->ni = *ni; - BUG_ON(ni->blk_addr != NULL_ADDR); - } - - if (new_blkaddr == NEW_ADDR) - e->checkpointed = false; - - /* sanity check */ - BUG_ON(nat_get_blkaddr(e) != ni->blk_addr); - BUG_ON(nat_get_blkaddr(e) == NULL_ADDR && - new_blkaddr == NULL_ADDR); - BUG_ON(nat_get_blkaddr(e) == NEW_ADDR && - new_blkaddr == NEW_ADDR); - BUG_ON(nat_get_blkaddr(e) != NEW_ADDR && - nat_get_blkaddr(e) != NULL_ADDR && - new_blkaddr == NEW_ADDR); - - /* increament version no as node is removed */ - if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) { - unsigned char version = nat_get_version(e); - nat_set_version(e, inc_node_version(version)); - } - - /* change address */ - nat_set_blkaddr(e, new_blkaddr); - __set_nat_cache_dirty(nm_i, e); - write_unlock(&nm_i->nat_tree_lock); -} - -static int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - - if (nm_i->nat_cnt < 2 * NM_WOUT_THRESHOLD) - return 0; - - write_lock(&nm_i->nat_tree_lock); - while (nr_shrink && !list_empty(&nm_i->nat_entries)) { - struct nat_entry *ne; - ne = list_first_entry(&nm_i->nat_entries, - struct nat_entry, list); - __del_from_nat_cache(nm_i, ne); - nr_shrink--; - } - write_unlock(&nm_i->nat_tree_lock); - return nr_shrink; -} - -/* - * This function returns always success - */ -void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - nid_t start_nid = START_NID(nid); - struct f2fs_nat_block *nat_blk; - struct page *page = NULL; - struct f2fs_nat_entry ne; - struct nat_entry *e; - int i; - - memset(&ne, 0, sizeof(struct f2fs_nat_entry)); - ni->nid = nid; - - /* Check nat cache */ - read_lock(&nm_i->nat_tree_lock); - e = __lookup_nat_cache(nm_i, nid); - if (e) { - ni->ino = nat_get_ino(e); - ni->blk_addr = nat_get_blkaddr(e); - ni->version = nat_get_version(e); - } - read_unlock(&nm_i->nat_tree_lock); - if (e) - return; - - /* Check current segment summary */ - mutex_lock(&curseg->curseg_mutex); - i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0); - if (i >= 0) { - ne = nat_in_journal(sum, i); - node_info_from_raw_nat(ni, &ne); - } - mutex_unlock(&curseg->curseg_mutex); - if (i >= 0) - goto cache; - - /* Fill node_info from nat page */ - page = get_current_nat_page(sbi, start_nid); - nat_blk = (struct f2fs_nat_block *)page_address(page); - ne = nat_blk->entries[nid - start_nid]; - node_info_from_raw_nat(ni, &ne); - f2fs_put_page(page, 1); -cache: - /* cache nat entry */ - cache_nat_entry(NM_I(sbi), nid, &ne); -} - -/* - * The maximum depth is four. - * Offset[0] will have raw inode offset. - */ -static int get_node_path(long block, int offset[4], unsigned int noffset[4]) -{ - const long direct_index = ADDRS_PER_INODE; - const long direct_blks = ADDRS_PER_BLOCK; - const long dptrs_per_blk = NIDS_PER_BLOCK; - const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK; - const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK; - int n = 0; - int level = 0; - - noffset[0] = 0; - - if (block < direct_index) { - offset[n++] = block; - level = 0; - goto got; - } - block -= direct_index; - if (block < direct_blks) { - offset[n++] = NODE_DIR1_BLOCK; - noffset[n] = 1; - offset[n++] = block; - level = 1; - goto got; - } - block -= direct_blks; - if (block < direct_blks) { - offset[n++] = NODE_DIR2_BLOCK; - noffset[n] = 2; - offset[n++] = block; - level = 1; - goto got; - } - block -= direct_blks; - if (block < indirect_blks) { - offset[n++] = NODE_IND1_BLOCK; - noffset[n] = 3; - offset[n++] = block / direct_blks; - noffset[n] = 4 + offset[n - 1]; - offset[n++] = block % direct_blks; - level = 2; - goto got; - } - block -= indirect_blks; - if (block < indirect_blks) { - offset[n++] = NODE_IND2_BLOCK; - noffset[n] = 4 + dptrs_per_blk; - offset[n++] = block / direct_blks; - noffset[n] = 5 + dptrs_per_blk + offset[n - 1]; - offset[n++] = block % direct_blks; - level = 2; - goto got; - } - block -= indirect_blks; - if (block < dindirect_blks) { - offset[n++] = NODE_DIND_BLOCK; - noffset[n] = 5 + (dptrs_per_blk * 2); - offset[n++] = block / indirect_blks; - noffset[n] = 6 + (dptrs_per_blk * 2) + - offset[n - 1] * (dptrs_per_blk + 1); - offset[n++] = (block / direct_blks) % dptrs_per_blk; - noffset[n] = 7 + (dptrs_per_blk * 2) + - offset[n - 2] * (dptrs_per_blk + 1) + - offset[n - 1]; - offset[n++] = block % direct_blks; - level = 3; - goto got; - } else { - BUG(); - } -got: - return level; -} - -/* - * Caller should call f2fs_put_dnode(dn). - */ -int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct page *npage[4]; - struct page *parent; - int offset[4]; - unsigned int noffset[4]; - nid_t nids[4]; - int level, i; - int err = 0; - - level = get_node_path(index, offset, noffset); - - nids[0] = dn->inode->i_ino; - npage[0] = get_node_page(sbi, nids[0]); - if (IS_ERR(npage[0])) - return PTR_ERR(npage[0]); - - parent = npage[0]; - nids[1] = get_nid(parent, offset[0], true); - dn->inode_page = npage[0]; - dn->inode_page_locked = true; - - /* get indirect or direct nodes */ - for (i = 1; i <= level; i++) { - bool done = false; - - if (!nids[i] && !ro) { - mutex_lock_op(sbi, NODE_NEW); - - /* alloc new node */ - if (!alloc_nid(sbi, &(nids[i]))) { - mutex_unlock_op(sbi, NODE_NEW); - err = -ENOSPC; - goto release_pages; - } - - dn->nid = nids[i]; - npage[i] = new_node_page(dn, noffset[i]); - if (IS_ERR(npage[i])) { - alloc_nid_failed(sbi, nids[i]); - mutex_unlock_op(sbi, NODE_NEW); - err = PTR_ERR(npage[i]); - goto release_pages; - } - - set_nid(parent, offset[i - 1], nids[i], i == 1); - alloc_nid_done(sbi, nids[i]); - mutex_unlock_op(sbi, NODE_NEW); - done = true; - } else if (ro && i == level && level > 1) { - npage[i] = get_node_page_ra(parent, offset[i - 1]); - if (IS_ERR(npage[i])) { - err = PTR_ERR(npage[i]); - goto release_pages; - } - done = true; - } - if (i == 1) { - dn->inode_page_locked = false; - unlock_page(parent); - } else { - f2fs_put_page(parent, 1); - } - - if (!done) { - npage[i] = get_node_page(sbi, nids[i]); - if (IS_ERR(npage[i])) { - err = PTR_ERR(npage[i]); - f2fs_put_page(npage[0], 0); - goto release_out; - } - } - if (i < level) { - parent = npage[i]; - nids[i + 1] = get_nid(parent, offset[i], false); - } - } - dn->nid = nids[level]; - dn->ofs_in_node = offset[level]; - dn->node_page = npage[level]; - dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node); - return 0; - -release_pages: - f2fs_put_page(parent, 1); - if (i > 1) - f2fs_put_page(npage[0], 0); -release_out: - dn->inode_page = NULL; - dn->node_page = NULL; - return err; -} - -static void truncate_node(struct dnode_of_data *dn) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct node_info ni; - - get_node_info(sbi, dn->nid, &ni); - BUG_ON(ni.blk_addr == NULL_ADDR); - - if (ni.blk_addr != NULL_ADDR) - invalidate_blocks(sbi, ni.blk_addr); - - /* Deallocate node address */ - dec_valid_node_count(sbi, dn->inode, 1); - set_node_addr(sbi, &ni, NULL_ADDR); - - if (dn->nid == dn->inode->i_ino) { - remove_orphan_inode(sbi, dn->nid); - dec_valid_inode_count(sbi); - } else { - sync_inode_page(dn); - } - - clear_node_page_dirty(dn->node_page); - F2FS_SET_SB_DIRT(sbi); - - f2fs_put_page(dn->node_page, 1); - dn->node_page = NULL; -} - -static int truncate_dnode(struct dnode_of_data *dn) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct page *page; - - if (dn->nid == 0) - return 1; - - /* get direct node */ - page = get_node_page(sbi, dn->nid); - if (IS_ERR(page) && PTR_ERR(page) == -ENOENT) - return 1; - else if (IS_ERR(page)) - return PTR_ERR(page); - - /* Make dnode_of_data for parameter */ - dn->node_page = page; - dn->ofs_in_node = 0; - truncate_data_blocks(dn); - truncate_node(dn); - return 1; -} - -static int truncate_nodes(struct dnode_of_data *dn, unsigned int nofs, - int ofs, int depth) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct dnode_of_data rdn = *dn; - struct page *page; - struct f2fs_node *rn; - nid_t child_nid; - unsigned int child_nofs; - int freed = 0; - int i, ret; - - if (dn->nid == 0) - return NIDS_PER_BLOCK + 1; - - page = get_node_page(sbi, dn->nid); - if (IS_ERR(page)) - return PTR_ERR(page); - - rn = (struct f2fs_node *)page_address(page); - if (depth < 3) { - for (i = ofs; i < NIDS_PER_BLOCK; i++, freed++) { - child_nid = le32_to_cpu(rn->in.nid[i]); - if (child_nid == 0) - continue; - rdn.nid = child_nid; - ret = truncate_dnode(&rdn); - if (ret < 0) - goto out_err; - set_nid(page, i, 0, false); - } - } else { - child_nofs = nofs + ofs * (NIDS_PER_BLOCK + 1) + 1; - for (i = ofs; i < NIDS_PER_BLOCK; i++) { - child_nid = le32_to_cpu(rn->in.nid[i]); - if (child_nid == 0) { - child_nofs += NIDS_PER_BLOCK + 1; - continue; - } - rdn.nid = child_nid; - ret = truncate_nodes(&rdn, child_nofs, 0, depth - 1); - if (ret == (NIDS_PER_BLOCK + 1)) { - set_nid(page, i, 0, false); - child_nofs += ret; - } else if (ret < 0 && ret != -ENOENT) { - goto out_err; - } - } - freed = child_nofs; - } - - if (!ofs) { - /* remove current indirect node */ - dn->node_page = page; - truncate_node(dn); - freed++; - } else { - f2fs_put_page(page, 1); - } - return freed; - -out_err: - f2fs_put_page(page, 1); - return ret; -} - -static int truncate_partial_nodes(struct dnode_of_data *dn, - struct f2fs_inode *ri, int *offset, int depth) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct page *pages[2]; - nid_t nid[3]; - nid_t child_nid; - int err = 0; - int i; - int idx = depth - 2; - - nid[0] = le32_to_cpu(ri->i_nid[offset[0] - NODE_DIR1_BLOCK]); - if (!nid[0]) - return 0; - - /* get indirect nodes in the path */ - for (i = 0; i < depth - 1; i++) { - /* refernece count'll be increased */ - pages[i] = get_node_page(sbi, nid[i]); - if (IS_ERR(pages[i])) { - depth = i + 1; - err = PTR_ERR(pages[i]); - goto fail; - } - nid[i + 1] = get_nid(pages[i], offset[i + 1], false); - } - - /* free direct nodes linked to a partial indirect node */ - for (i = offset[depth - 1]; i < NIDS_PER_BLOCK; i++) { - child_nid = get_nid(pages[idx], i, false); - if (!child_nid) - continue; - dn->nid = child_nid; - err = truncate_dnode(dn); - if (err < 0) - goto fail; - set_nid(pages[idx], i, 0, false); - } - - if (offset[depth - 1] == 0) { - dn->node_page = pages[idx]; - dn->nid = nid[idx]; - truncate_node(dn); - } else { - f2fs_put_page(pages[idx], 1); - } - offset[idx]++; - offset[depth - 1] = 0; -fail: - for (i = depth - 3; i >= 0; i--) - f2fs_put_page(pages[i], 1); - return err; -} - -/* - * All the block addresses of data and nodes should be nullified. - */ -int truncate_inode_blocks(struct inode *inode, pgoff_t from) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - int err = 0, cont = 1; - int level, offset[4], noffset[4]; - unsigned int nofs; - struct f2fs_node *rn; - struct dnode_of_data dn; - struct page *page; - - level = get_node_path(from, offset, noffset); - - page = get_node_page(sbi, inode->i_ino); - if (IS_ERR(page)) - return PTR_ERR(page); - - set_new_dnode(&dn, inode, page, NULL, 0); - unlock_page(page); - - rn = page_address(page); - switch (level) { - case 0: - case 1: - nofs = noffset[1]; - break; - case 2: - nofs = noffset[1]; - if (!offset[level - 1]) - goto skip_partial; - err = truncate_partial_nodes(&dn, &rn->i, offset, level); - if (err < 0 && err != -ENOENT) - goto fail; - nofs += 1 + NIDS_PER_BLOCK; - break; - case 3: - nofs = 5 + 2 * NIDS_PER_BLOCK; - if (!offset[level - 1]) - goto skip_partial; - err = truncate_partial_nodes(&dn, &rn->i, offset, level); - if (err < 0 && err != -ENOENT) - goto fail; - break; - default: - BUG(); - } - -skip_partial: - while (cont) { - dn.nid = le32_to_cpu(rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK]); - switch (offset[0]) { - case NODE_DIR1_BLOCK: - case NODE_DIR2_BLOCK: - err = truncate_dnode(&dn); - break; - - case NODE_IND1_BLOCK: - case NODE_IND2_BLOCK: - err = truncate_nodes(&dn, nofs, offset[1], 2); - break; - - case NODE_DIND_BLOCK: - err = truncate_nodes(&dn, nofs, offset[1], 3); - cont = 0; - break; - - default: - BUG(); - } - if (err < 0 && err != -ENOENT) - goto fail; - if (offset[1] == 0 && - rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK]) { - lock_page(page); - wait_on_page_writeback(page); - rn->i.i_nid[offset[0] - NODE_DIR1_BLOCK] = 0; - set_page_dirty(page); - unlock_page(page); - } - offset[1] = 0; - offset[0]++; - nofs += err; - } -fail: - f2fs_put_page(page, 0); - return err > 0 ? 0 : err; -} - -int remove_inode_page(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct page *page; - nid_t ino = inode->i_ino; - struct dnode_of_data dn; - - mutex_lock_op(sbi, NODE_TRUNC); - page = get_node_page(sbi, ino); - if (IS_ERR(page)) { - mutex_unlock_op(sbi, NODE_TRUNC); - return PTR_ERR(page); - } - - if (F2FS_I(inode)->i_xattr_nid) { - nid_t nid = F2FS_I(inode)->i_xattr_nid; - struct page *npage = get_node_page(sbi, nid); - - if (IS_ERR(npage)) { - mutex_unlock_op(sbi, NODE_TRUNC); - return PTR_ERR(npage); - } - - F2FS_I(inode)->i_xattr_nid = 0; - set_new_dnode(&dn, inode, page, npage, nid); - dn.inode_page_locked = 1; - truncate_node(&dn); - } - if (inode->i_blocks == 1) { - /* inernally call f2fs_put_page() */ - set_new_dnode(&dn, inode, page, page, ino); - truncate_node(&dn); - } else if (inode->i_blocks == 0) { - struct node_info ni; - get_node_info(sbi, inode->i_ino, &ni); - - /* called after f2fs_new_inode() is failed */ - BUG_ON(ni.blk_addr != NULL_ADDR); - f2fs_put_page(page, 1); - } else { - BUG(); - } - mutex_unlock_op(sbi, NODE_TRUNC); - return 0; -} - -int new_inode_page(struct inode *inode, struct dentry *dentry) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct page *page; - struct dnode_of_data dn; - - /* allocate inode page for new inode */ - set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); - mutex_lock_op(sbi, NODE_NEW); - page = new_node_page(&dn, 0); - init_dent_inode(dentry, page); - mutex_unlock_op(sbi, NODE_NEW); - if (IS_ERR(page)) - return PTR_ERR(page); - f2fs_put_page(page, 1); - return 0; -} - -struct page *new_node_page(struct dnode_of_data *dn, unsigned int ofs) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); - struct address_space *mapping = sbi->node_inode->i_mapping; - struct node_info old_ni, new_ni; - struct page *page; - int err; - - if (is_inode_flag_set(F2FS_I(dn->inode), FI_NO_ALLOC)) - return ERR_PTR(-EPERM); - - page = grab_cache_page(mapping, dn->nid); - if (!page) - return ERR_PTR(-ENOMEM); - - get_node_info(sbi, dn->nid, &old_ni); - - SetPageUptodate(page); - fill_node_footer(page, dn->nid, dn->inode->i_ino, ofs, true); - - /* Reinitialize old_ni with new node page */ - BUG_ON(old_ni.blk_addr != NULL_ADDR); - new_ni = old_ni; - new_ni.ino = dn->inode->i_ino; - - if (!inc_valid_node_count(sbi, dn->inode, 1)) { - err = -ENOSPC; - goto fail; - } - set_node_addr(sbi, &new_ni, NEW_ADDR); - - dn->node_page = page; - sync_inode_page(dn); - set_page_dirty(page); - set_cold_node(dn->inode, page); - if (ofs == 0) - inc_valid_inode_count(sbi); - - return page; - -fail: - f2fs_put_page(page, 1); - return ERR_PTR(err); -} - -static int read_node_page(struct page *page, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); - struct node_info ni; - - get_node_info(sbi, page->index, &ni); - - if (ni.blk_addr == NULL_ADDR) - return -ENOENT; - return f2fs_readpage(sbi, page, ni.blk_addr, type); -} - -/* - * Readahead a node page - */ -void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid) -{ - struct address_space *mapping = sbi->node_inode->i_mapping; - struct page *apage; - - apage = find_get_page(mapping, nid); - if (apage && PageUptodate(apage)) - goto release_out; - f2fs_put_page(apage, 0); - - apage = grab_cache_page(mapping, nid); - if (!apage) - return; - - if (read_node_page(apage, READA)) - goto unlock_out; - - page_cache_release(apage); - return; - -unlock_out: - unlock_page(apage); -release_out: - page_cache_release(apage); -} - -struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) -{ - int err; - struct page *page; - struct address_space *mapping = sbi->node_inode->i_mapping; - - page = grab_cache_page(mapping, nid); - if (!page) - return ERR_PTR(-ENOMEM); - - err = read_node_page(page, READ_SYNC); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - - BUG_ON(nid != nid_of_node(page)); - mark_page_accessed(page); - return page; -} - -/* - * Return a locked page for the desired node page. - * And, readahead MAX_RA_NODE number of node pages. - */ -struct page *get_node_page_ra(struct page *parent, int start) -{ - struct f2fs_sb_info *sbi = F2FS_SB(parent->mapping->host->i_sb); - struct address_space *mapping = sbi->node_inode->i_mapping; - int i, end; - int err = 0; - nid_t nid; - struct page *page; - - /* First, try getting the desired direct node. */ - nid = get_nid(parent, start, false); - if (!nid) - return ERR_PTR(-ENOENT); - - page = find_get_page(mapping, nid); - if (page && PageUptodate(page)) - goto page_hit; - f2fs_put_page(page, 0); - -repeat: - page = grab_cache_page(mapping, nid); - if (!page) - return ERR_PTR(-ENOMEM); - - err = read_node_page(page, READA); - if (err) { - f2fs_put_page(page, 1); - return ERR_PTR(err); - } - - /* Then, try readahead for siblings of the desired node */ - end = start + MAX_RA_NODE; - end = min(end, NIDS_PER_BLOCK); - for (i = start + 1; i < end; i++) { - nid = get_nid(parent, i, false); - if (!nid) - continue; - ra_node_page(sbi, nid); - } - -page_hit: - lock_page(page); - if (PageError(page)) { - f2fs_put_page(page, 1); - return ERR_PTR(-EIO); - } - - /* Has the page been truncated? */ - if (page->mapping != mapping) { - f2fs_put_page(page, 1); - goto repeat; - } - return page; -} - -void sync_inode_page(struct dnode_of_data *dn) -{ - if (IS_INODE(dn->node_page) || dn->inode_page == dn->node_page) { - update_inode(dn->inode, dn->node_page); - } else if (dn->inode_page) { - if (!dn->inode_page_locked) - lock_page(dn->inode_page); - update_inode(dn->inode, dn->inode_page); - if (!dn->inode_page_locked) - unlock_page(dn->inode_page); - } else { - f2fs_write_inode(dn->inode, NULL); - } -} - -int sync_node_pages(struct f2fs_sb_info *sbi, nid_t ino, - struct writeback_control *wbc) -{ - struct address_space *mapping = sbi->node_inode->i_mapping; - pgoff_t index, end; - struct pagevec pvec; - int step = ino ? 2 : 0; - int nwritten = 0, wrote = 0; - - pagevec_init(&pvec, 0); - -next_step: - index = 0; - end = LONG_MAX; - - while (index <= end) { - int i, nr_pages; - nr_pages = pagevec_lookup_tag(&pvec, mapping, &index, - PAGECACHE_TAG_DIRTY, - min(end - index, (pgoff_t)PAGEVEC_SIZE-1) + 1); - if (nr_pages == 0) - break; - - for (i = 0; i < nr_pages; i++) { - struct page *page = pvec.pages[i]; - - /* - * flushing sequence with step: - * 0. indirect nodes - * 1. dentry dnodes - * 2. file dnodes - */ - if (step == 0 && IS_DNODE(page)) - continue; - if (step == 1 && (!IS_DNODE(page) || - is_cold_node(page))) - continue; - if (step == 2 && (!IS_DNODE(page) || - !is_cold_node(page))) - continue; - - /* - * If an fsync mode, - * we should not skip writing node pages. - */ - if (ino && ino_of_node(page) == ino) - lock_page(page); - else if (!trylock_page(page)) - continue; - - if (unlikely(page->mapping != mapping)) { -continue_unlock: - unlock_page(page); - continue; - } - if (ino && ino_of_node(page) != ino) - goto continue_unlock; - - if (!PageDirty(page)) { - /* someone wrote it for us */ - goto continue_unlock; - } - - if (!clear_page_dirty_for_io(page)) - goto continue_unlock; - - /* called by fsync() */ - if (ino && IS_DNODE(page)) { - int mark = !is_checkpointed_node(sbi, ino); - set_fsync_mark(page, 1); - if (IS_INODE(page)) - set_dentry_mark(page, mark); - nwritten++; - } else { - set_fsync_mark(page, 0); - set_dentry_mark(page, 0); - } - mapping->a_ops->writepage(page, wbc); - wrote++; - - if (--wbc->nr_to_write == 0) - break; - } - pagevec_release(&pvec); - cond_resched(); - - if (wbc->nr_to_write == 0) { - step = 2; - break; - } - } - - if (step < 2) { - step++; - goto next_step; - } - - if (wrote) - f2fs_submit_bio(sbi, NODE, wbc->sync_mode == WB_SYNC_ALL); - - return nwritten; -} - -static int f2fs_write_node_page(struct page *page, - struct writeback_control *wbc) -{ - struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); - nid_t nid; - unsigned int nofs; - block_t new_addr; - struct node_info ni; - - if (wbc->for_reclaim) { - dec_page_count(sbi, F2FS_DIRTY_NODES); - wbc->pages_skipped++; - set_page_dirty(page); - return AOP_WRITEPAGE_ACTIVATE; - } - - wait_on_page_writeback(page); - - mutex_lock_op(sbi, NODE_WRITE); - - /* get old block addr of this node page */ - nid = nid_of_node(page); - nofs = ofs_of_node(page); - BUG_ON(page->index != nid); - - get_node_info(sbi, nid, &ni); - - /* This page is already truncated */ - if (ni.blk_addr == NULL_ADDR) - return 0; - - set_page_writeback(page); - - /* insert node offset */ - write_node_page(sbi, page, nid, ni.blk_addr, &new_addr); - set_node_addr(sbi, &ni, new_addr); - dec_page_count(sbi, F2FS_DIRTY_NODES); - - mutex_unlock_op(sbi, NODE_WRITE); - unlock_page(page); - return 0; -} - -static int f2fs_write_node_pages(struct address_space *mapping, - struct writeback_control *wbc) -{ - struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); - struct block_device *bdev = sbi->sb->s_bdev; - long nr_to_write = wbc->nr_to_write; - - if (wbc->for_kupdate) - return 0; - - if (get_pages(sbi, F2FS_DIRTY_NODES) == 0) - return 0; - - if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK)) { - write_checkpoint(sbi, false, false); - return 0; - } - - /* if mounting is failed, skip writing node pages */ - wbc->nr_to_write = bio_get_nr_vecs(bdev); - sync_node_pages(sbi, 0, wbc); - wbc->nr_to_write = nr_to_write - - (bio_get_nr_vecs(bdev) - wbc->nr_to_write); - return 0; -} - -static int f2fs_set_node_page_dirty(struct page *page) -{ - struct address_space *mapping = page->mapping; - struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb); - - SetPageUptodate(page); - if (!PageDirty(page)) { - __set_page_dirty_nobuffers(page); - inc_page_count(sbi, F2FS_DIRTY_NODES); - SetPagePrivate(page); - return 1; - } - return 0; -} - -static void f2fs_invalidate_node_page(struct page *page, unsigned long offset) -{ - struct inode *inode = page->mapping->host; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - if (PageDirty(page)) - dec_page_count(sbi, F2FS_DIRTY_NODES); - ClearPagePrivate(page); -} - -static int f2fs_release_node_page(struct page *page, gfp_t wait) -{ - ClearPagePrivate(page); - return 0; -} - -/* - * Structure of the f2fs node operations - */ -const struct address_space_operations f2fs_node_aops = { - .writepage = f2fs_write_node_page, - .writepages = f2fs_write_node_pages, - .set_page_dirty = f2fs_set_node_page_dirty, - .invalidatepage = f2fs_invalidate_node_page, - .releasepage = f2fs_release_node_page, -}; - -static struct free_nid *__lookup_free_nid_list(nid_t n, struct list_head *head) -{ - struct list_head *this; - struct free_nid *i = NULL; - list_for_each(this, head) { - i = list_entry(this, struct free_nid, list); - if (i->nid == n) - break; - i = NULL; - } - return i; -} - -static void __del_from_free_nid_list(struct free_nid *i) -{ - list_del(&i->list); - kmem_cache_free(free_nid_slab, i); -} - -static int add_free_nid(struct f2fs_nm_info *nm_i, nid_t nid) -{ - struct free_nid *i; - - if (nm_i->fcnt > 2 * MAX_FREE_NIDS) - return 0; -retry: - i = kmem_cache_alloc(free_nid_slab, GFP_NOFS); - if (!i) { - cond_resched(); - goto retry; - } - i->nid = nid; - i->state = NID_NEW; - - spin_lock(&nm_i->free_nid_list_lock); - if (__lookup_free_nid_list(nid, &nm_i->free_nid_list)) { - spin_unlock(&nm_i->free_nid_list_lock); - kmem_cache_free(free_nid_slab, i); - return 0; - } - list_add_tail(&i->list, &nm_i->free_nid_list); - nm_i->fcnt++; - spin_unlock(&nm_i->free_nid_list_lock); - return 1; -} - -static void remove_free_nid(struct f2fs_nm_info *nm_i, nid_t nid) -{ - struct free_nid *i; - spin_lock(&nm_i->free_nid_list_lock); - i = __lookup_free_nid_list(nid, &nm_i->free_nid_list); - if (i && i->state == NID_NEW) { - __del_from_free_nid_list(i); - nm_i->fcnt--; - } - spin_unlock(&nm_i->free_nid_list_lock); -} - -static int scan_nat_page(struct f2fs_nm_info *nm_i, - struct page *nat_page, nid_t start_nid) -{ - struct f2fs_nat_block *nat_blk = page_address(nat_page); - block_t blk_addr; - int fcnt = 0; - int i; - - /* 0 nid should not be used */ - if (start_nid == 0) - ++start_nid; - - i = start_nid % NAT_ENTRY_PER_BLOCK; - - for (; i < NAT_ENTRY_PER_BLOCK; i++, start_nid++) { - blk_addr = le32_to_cpu(nat_blk->entries[i].block_addr); - BUG_ON(blk_addr == NEW_ADDR); - if (blk_addr == NULL_ADDR) - fcnt += add_free_nid(nm_i, start_nid); - } - return fcnt; -} - -static void build_free_nids(struct f2fs_sb_info *sbi) -{ - struct free_nid *fnid, *next_fnid; - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - nid_t nid = 0; - bool is_cycled = false; - int fcnt = 0; - int i; - - nid = nm_i->next_scan_nid; - nm_i->init_scan_nid = nid; - - ra_nat_pages(sbi, nid); - - while (1) { - struct page *page = get_current_nat_page(sbi, nid); - - fcnt += scan_nat_page(nm_i, page, nid); - f2fs_put_page(page, 1); - - nid += (NAT_ENTRY_PER_BLOCK - (nid % NAT_ENTRY_PER_BLOCK)); - - if (nid >= nm_i->max_nid) { - nid = 0; - is_cycled = true; - } - if (fcnt > MAX_FREE_NIDS) - break; - if (is_cycled && nm_i->init_scan_nid <= nid) - break; - } - - nm_i->next_scan_nid = nid; - - /* find free nids from current sum_pages */ - mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < nats_in_cursum(sum); i++) { - block_t addr = le32_to_cpu(nat_in_journal(sum, i).block_addr); - nid = le32_to_cpu(nid_in_journal(sum, i)); - if (addr == NULL_ADDR) - add_free_nid(nm_i, nid); - else - remove_free_nid(nm_i, nid); - } - mutex_unlock(&curseg->curseg_mutex); - - /* remove the free nids from current allocated nids */ - list_for_each_entry_safe(fnid, next_fnid, &nm_i->free_nid_list, list) { - struct nat_entry *ne; - - read_lock(&nm_i->nat_tree_lock); - ne = __lookup_nat_cache(nm_i, fnid->nid); - if (ne && nat_get_blkaddr(ne) != NULL_ADDR) - remove_free_nid(nm_i, fnid->nid); - read_unlock(&nm_i->nat_tree_lock); - } -} - -/* - * If this function returns success, caller can obtain a new nid - * from second parameter of this function. - * The returned nid could be used ino as well as nid when inode is created. - */ -bool alloc_nid(struct f2fs_sb_info *sbi, nid_t *nid) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct free_nid *i = NULL; - struct list_head *this; -retry: - mutex_lock(&nm_i->build_lock); - if (!nm_i->fcnt) { - /* scan NAT in order to build free nid list */ - build_free_nids(sbi); - if (!nm_i->fcnt) { - mutex_unlock(&nm_i->build_lock); - return false; - } - } - mutex_unlock(&nm_i->build_lock); - - /* - * We check fcnt again since previous check is racy as - * we didn't hold free_nid_list_lock. So other thread - * could consume all of free nids. - */ - spin_lock(&nm_i->free_nid_list_lock); - if (!nm_i->fcnt) { - spin_unlock(&nm_i->free_nid_list_lock); - goto retry; - } - - BUG_ON(list_empty(&nm_i->free_nid_list)); - list_for_each(this, &nm_i->free_nid_list) { - i = list_entry(this, struct free_nid, list); - if (i->state == NID_NEW) - break; - } - - BUG_ON(i->state != NID_NEW); - *nid = i->nid; - i->state = NID_ALLOC; - nm_i->fcnt--; - spin_unlock(&nm_i->free_nid_list_lock); - return true; -} - -/* - * alloc_nid() should be called prior to this function. - */ -void alloc_nid_done(struct f2fs_sb_info *sbi, nid_t nid) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct free_nid *i; - - spin_lock(&nm_i->free_nid_list_lock); - i = __lookup_free_nid_list(nid, &nm_i->free_nid_list); - if (i) { - BUG_ON(i->state != NID_ALLOC); - __del_from_free_nid_list(i); - } - spin_unlock(&nm_i->free_nid_list_lock); -} - -/* - * alloc_nid() should be called prior to this function. - */ -void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid) -{ - alloc_nid_done(sbi, nid); - add_free_nid(NM_I(sbi), nid); -} - -void recover_node_page(struct f2fs_sb_info *sbi, struct page *page, - struct f2fs_summary *sum, struct node_info *ni, - block_t new_blkaddr) -{ - rewrite_node_page(sbi, page, sum, ni->blk_addr, new_blkaddr); - set_node_addr(sbi, ni, new_blkaddr); - clear_node_page_dirty(page); -} - -int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) -{ - struct address_space *mapping = sbi->node_inode->i_mapping; - struct f2fs_node *src, *dst; - nid_t ino = ino_of_node(page); - struct node_info old_ni, new_ni; - struct page *ipage; - - ipage = grab_cache_page(mapping, ino); - if (!ipage) - return -ENOMEM; - - /* Should not use this inode from free nid list */ - remove_free_nid(NM_I(sbi), ino); - - get_node_info(sbi, ino, &old_ni); - SetPageUptodate(ipage); - fill_node_footer(ipage, ino, ino, 0, true); - - src = (struct f2fs_node *)page_address(page); - dst = (struct f2fs_node *)page_address(ipage); - - memcpy(dst, src, (unsigned long)&src->i.i_ext - (unsigned long)&src->i); - dst->i.i_size = 0; - dst->i.i_blocks = cpu_to_le64(1); - dst->i.i_links = cpu_to_le32(1); - dst->i.i_xattr_nid = 0; - - new_ni = old_ni; - new_ni.ino = ino; - - set_node_addr(sbi, &new_ni, NEW_ADDR); - inc_valid_inode_count(sbi); - - f2fs_put_page(ipage, 1); - return 0; -} - -int restore_node_summary(struct f2fs_sb_info *sbi, - unsigned int segno, struct f2fs_summary_block *sum) -{ - struct f2fs_node *rn; - struct f2fs_summary *sum_entry; - struct page *page; - block_t addr; - int i, last_offset; - - /* alloc temporal page for read node */ - page = alloc_page(GFP_NOFS | __GFP_ZERO); - if (IS_ERR(page)) - return PTR_ERR(page); - lock_page(page); - - /* scan the node segment */ - last_offset = sbi->blocks_per_seg; - addr = START_BLOCK(sbi, segno); - sum_entry = &sum->entries[0]; - - for (i = 0; i < last_offset; i++, sum_entry++) { - if (f2fs_readpage(sbi, page, addr, READ_SYNC)) - goto out; - - rn = (struct f2fs_node *)page_address(page); - sum_entry->nid = rn->footer.nid; - sum_entry->version = 0; - sum_entry->ofs_in_node = 0; - addr++; - - /* - * In order to read next node page, - * we must clear PageUptodate flag. - */ - ClearPageUptodate(page); - } -out: - unlock_page(page); - __free_pages(page, 0); - return 0; -} - -static bool flush_nats_in_journal(struct f2fs_sb_info *sbi) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - int i; - - mutex_lock(&curseg->curseg_mutex); - - if (nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES) { - mutex_unlock(&curseg->curseg_mutex); - return false; - } - - for (i = 0; i < nats_in_cursum(sum); i++) { - struct nat_entry *ne; - struct f2fs_nat_entry raw_ne; - nid_t nid = le32_to_cpu(nid_in_journal(sum, i)); - - raw_ne = nat_in_journal(sum, i); -retry: - write_lock(&nm_i->nat_tree_lock); - ne = __lookup_nat_cache(nm_i, nid); - if (ne) { - __set_nat_cache_dirty(nm_i, ne); - write_unlock(&nm_i->nat_tree_lock); - continue; - } - ne = grab_nat_entry(nm_i, nid); - if (!ne) { - write_unlock(&nm_i->nat_tree_lock); - goto retry; - } - nat_set_blkaddr(ne, le32_to_cpu(raw_ne.block_addr)); - nat_set_ino(ne, le32_to_cpu(raw_ne.ino)); - nat_set_version(ne, raw_ne.version); - __set_nat_cache_dirty(nm_i, ne); - write_unlock(&nm_i->nat_tree_lock); - } - update_nats_in_cursum(sum, -i); - mutex_unlock(&curseg->curseg_mutex); - return true; -} - -/* - * This function is called during the checkpointing process. - */ -void flush_nat_entries(struct f2fs_sb_info *sbi) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - struct list_head *cur, *n; - struct page *page = NULL; - struct f2fs_nat_block *nat_blk = NULL; - nid_t start_nid = 0, end_nid = 0; - bool flushed; - - flushed = flush_nats_in_journal(sbi); - - if (!flushed) - mutex_lock(&curseg->curseg_mutex); - - /* 1) flush dirty nat caches */ - list_for_each_safe(cur, n, &nm_i->dirty_nat_entries) { - struct nat_entry *ne; - nid_t nid; - struct f2fs_nat_entry raw_ne; - int offset = -1; - block_t old_blkaddr, new_blkaddr; - - ne = list_entry(cur, struct nat_entry, list); - nid = nat_get_nid(ne); - - if (nat_get_blkaddr(ne) == NEW_ADDR) - continue; - if (flushed) - goto to_nat_page; - - /* if there is room for nat enries in curseg->sumpage */ - offset = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 1); - if (offset >= 0) { - raw_ne = nat_in_journal(sum, offset); - old_blkaddr = le32_to_cpu(raw_ne.block_addr); - goto flush_now; - } -to_nat_page: - if (!page || (start_nid > nid || nid > end_nid)) { - if (page) { - f2fs_put_page(page, 1); - page = NULL; - } - start_nid = START_NID(nid); - end_nid = start_nid + NAT_ENTRY_PER_BLOCK - 1; - - /* - * get nat block with dirty flag, increased reference - * count, mapped and lock - */ - page = get_next_nat_page(sbi, start_nid); - nat_blk = page_address(page); - } - - BUG_ON(!nat_blk); - raw_ne = nat_blk->entries[nid - start_nid]; - old_blkaddr = le32_to_cpu(raw_ne.block_addr); -flush_now: - new_blkaddr = nat_get_blkaddr(ne); - - raw_ne.ino = cpu_to_le32(nat_get_ino(ne)); - raw_ne.block_addr = cpu_to_le32(new_blkaddr); - raw_ne.version = nat_get_version(ne); - - if (offset < 0) { - nat_blk->entries[nid - start_nid] = raw_ne; - } else { - nat_in_journal(sum, offset) = raw_ne; - nid_in_journal(sum, offset) = cpu_to_le32(nid); - } - - if (nat_get_blkaddr(ne) == NULL_ADDR) { - write_lock(&nm_i->nat_tree_lock); - __del_from_nat_cache(nm_i, ne); - write_unlock(&nm_i->nat_tree_lock); - - /* We can reuse this freed nid at this point */ - add_free_nid(NM_I(sbi), nid); - } else { - write_lock(&nm_i->nat_tree_lock); - __clear_nat_cache_dirty(nm_i, ne); - ne->checkpointed = true; - write_unlock(&nm_i->nat_tree_lock); - } - } - if (!flushed) - mutex_unlock(&curseg->curseg_mutex); - f2fs_put_page(page, 1); - - /* 2) shrink nat caches if necessary */ - try_to_free_nats(sbi, nm_i->nat_cnt - NM_WOUT_THRESHOLD); -} - -static int init_node_manager(struct f2fs_sb_info *sbi) -{ - struct f2fs_super_block *sb_raw = F2FS_RAW_SUPER(sbi); - struct f2fs_nm_info *nm_i = NM_I(sbi); - unsigned char *version_bitmap; - unsigned int nat_segs, nat_blocks; - - nm_i->nat_blkaddr = le32_to_cpu(sb_raw->nat_blkaddr); - - /* segment_count_nat includes pair segment so divide to 2. */ - nat_segs = le32_to_cpu(sb_raw->segment_count_nat) >> 1; - nat_blocks = nat_segs << le32_to_cpu(sb_raw->log_blocks_per_seg); - nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks; - nm_i->fcnt = 0; - nm_i->nat_cnt = 0; - - INIT_LIST_HEAD(&nm_i->free_nid_list); - INIT_RADIX_TREE(&nm_i->nat_root, GFP_ATOMIC); - INIT_LIST_HEAD(&nm_i->nat_entries); - INIT_LIST_HEAD(&nm_i->dirty_nat_entries); - - mutex_init(&nm_i->build_lock); - spin_lock_init(&nm_i->free_nid_list_lock); - rwlock_init(&nm_i->nat_tree_lock); - - nm_i->bitmap_size = __bitmap_size(sbi, NAT_BITMAP); - nm_i->init_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); - nm_i->next_scan_nid = le32_to_cpu(sbi->ckpt->next_free_nid); - - nm_i->nat_bitmap = kzalloc(nm_i->bitmap_size, GFP_KERNEL); - if (!nm_i->nat_bitmap) - return -ENOMEM; - version_bitmap = __bitmap_ptr(sbi, NAT_BITMAP); - if (!version_bitmap) - return -EFAULT; - - /* copy version bitmap */ - memcpy(nm_i->nat_bitmap, version_bitmap, nm_i->bitmap_size); - return 0; -} - -int build_node_manager(struct f2fs_sb_info *sbi) -{ - int err; - - sbi->nm_info = kzalloc(sizeof(struct f2fs_nm_info), GFP_KERNEL); - if (!sbi->nm_info) - return -ENOMEM; - - err = init_node_manager(sbi); - if (err) - return err; - - build_free_nids(sbi); - return 0; -} - -void destroy_node_manager(struct f2fs_sb_info *sbi) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct free_nid *i, *next_i; - struct nat_entry *natvec[NATVEC_SIZE]; - nid_t nid = 0; - unsigned int found; - - if (!nm_i) - return; - - /* destroy free nid list */ - spin_lock(&nm_i->free_nid_list_lock); - list_for_each_entry_safe(i, next_i, &nm_i->free_nid_list, list) { - BUG_ON(i->state == NID_ALLOC); - __del_from_free_nid_list(i); - nm_i->fcnt--; - } - BUG_ON(nm_i->fcnt); - spin_unlock(&nm_i->free_nid_list_lock); - - /* destroy nat cache */ - write_lock(&nm_i->nat_tree_lock); - while ((found = __gang_lookup_nat_cache(nm_i, - nid, NATVEC_SIZE, natvec))) { - unsigned idx; - for (idx = 0; idx < found; idx++) { - struct nat_entry *e = natvec[idx]; - nid = nat_get_nid(e) + 1; - __del_from_nat_cache(nm_i, e); - } - } - BUG_ON(nm_i->nat_cnt); - write_unlock(&nm_i->nat_tree_lock); - - kfree(nm_i->nat_bitmap); - sbi->nm_info = NULL; - kfree(nm_i); -} - -int create_node_manager_caches(void) -{ - nat_entry_slab = f2fs_kmem_cache_create("nat_entry", - sizeof(struct nat_entry), NULL); - if (!nat_entry_slab) - return -ENOMEM; - - free_nid_slab = f2fs_kmem_cache_create("free_nid", - sizeof(struct free_nid), NULL); - if (!free_nid_slab) { - kmem_cache_destroy(nat_entry_slab); - return -ENOMEM; - } - return 0; -} - -void destroy_node_manager_caches(void) -{ - kmem_cache_destroy(free_nid_slab); - kmem_cache_destroy(nat_entry_slab); -} diff --git a/trunk/fs/f2fs/node.h b/trunk/fs/f2fs/node.h deleted file mode 100644 index afdb130f782e..000000000000 --- a/trunk/fs/f2fs/node.h +++ /dev/null @@ -1,353 +0,0 @@ -/* - * fs/f2fs/node.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -/* start node id of a node block dedicated to the given node id */ -#define START_NID(nid) ((nid / NAT_ENTRY_PER_BLOCK) * NAT_ENTRY_PER_BLOCK) - -/* node block offset on the NAT area dedicated to the given start node id */ -#define NAT_BLOCK_OFFSET(start_nid) (start_nid / NAT_ENTRY_PER_BLOCK) - -/* # of pages to perform readahead before building free nids */ -#define FREE_NID_PAGES 4 - -/* maximum # of free node ids to produce during build_free_nids */ -#define MAX_FREE_NIDS (NAT_ENTRY_PER_BLOCK * FREE_NID_PAGES) - -/* maximum readahead size for node during getting data blocks */ -#define MAX_RA_NODE 128 - -/* maximum cached nat entries to manage memory footprint */ -#define NM_WOUT_THRESHOLD (64 * NAT_ENTRY_PER_BLOCK) - -/* vector size for gang look-up from nat cache that consists of radix tree */ -#define NATVEC_SIZE 64 - -/* - * For node information - */ -struct node_info { - nid_t nid; /* node id */ - nid_t ino; /* inode number of the node's owner */ - block_t blk_addr; /* block address of the node */ - unsigned char version; /* version of the node */ -}; - -struct nat_entry { - struct list_head list; /* for clean or dirty nat list */ - bool checkpointed; /* whether it is checkpointed or not */ - struct node_info ni; /* in-memory node information */ -}; - -#define nat_get_nid(nat) (nat->ni.nid) -#define nat_set_nid(nat, n) (nat->ni.nid = n) -#define nat_get_blkaddr(nat) (nat->ni.blk_addr) -#define nat_set_blkaddr(nat, b) (nat->ni.blk_addr = b) -#define nat_get_ino(nat) (nat->ni.ino) -#define nat_set_ino(nat, i) (nat->ni.ino = i) -#define nat_get_version(nat) (nat->ni.version) -#define nat_set_version(nat, v) (nat->ni.version = v) - -#define __set_nat_cache_dirty(nm_i, ne) \ - list_move_tail(&ne->list, &nm_i->dirty_nat_entries); -#define __clear_nat_cache_dirty(nm_i, ne) \ - list_move_tail(&ne->list, &nm_i->nat_entries); -#define inc_node_version(version) (++version) - -static inline void node_info_from_raw_nat(struct node_info *ni, - struct f2fs_nat_entry *raw_ne) -{ - ni->ino = le32_to_cpu(raw_ne->ino); - ni->blk_addr = le32_to_cpu(raw_ne->block_addr); - ni->version = raw_ne->version; -} - -/* - * For free nid mangement - */ -enum nid_state { - NID_NEW, /* newly added to free nid list */ - NID_ALLOC /* it is allocated */ -}; - -struct free_nid { - struct list_head list; /* for free node id list */ - nid_t nid; /* node id */ - int state; /* in use or not: NID_NEW or NID_ALLOC */ -}; - -static inline int next_free_nid(struct f2fs_sb_info *sbi, nid_t *nid) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - struct free_nid *fnid; - - if (nm_i->fcnt <= 0) - return -1; - spin_lock(&nm_i->free_nid_list_lock); - fnid = list_entry(nm_i->free_nid_list.next, struct free_nid, list); - *nid = fnid->nid; - spin_unlock(&nm_i->free_nid_list_lock); - return 0; -} - -/* - * inline functions - */ -static inline void get_nat_bitmap(struct f2fs_sb_info *sbi, void *addr) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - memcpy(addr, nm_i->nat_bitmap, nm_i->bitmap_size); -} - -static inline pgoff_t current_nat_addr(struct f2fs_sb_info *sbi, nid_t start) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - pgoff_t block_off; - pgoff_t block_addr; - int seg_off; - - block_off = NAT_BLOCK_OFFSET(start); - seg_off = block_off >> sbi->log_blocks_per_seg; - - block_addr = (pgoff_t)(nm_i->nat_blkaddr + - (seg_off << sbi->log_blocks_per_seg << 1) + - (block_off & ((1 << sbi->log_blocks_per_seg) - 1))); - - if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) - block_addr += sbi->blocks_per_seg; - - return block_addr; -} - -static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi, - pgoff_t block_addr) -{ - struct f2fs_nm_info *nm_i = NM_I(sbi); - - block_addr -= nm_i->nat_blkaddr; - if ((block_addr >> sbi->log_blocks_per_seg) % 2) - block_addr -= sbi->blocks_per_seg; - else - block_addr += sbi->blocks_per_seg; - - return block_addr + nm_i->nat_blkaddr; -} - -static inline void set_to_next_nat(struct f2fs_nm_info *nm_i, nid_t start_nid) -{ - unsigned int block_off = NAT_BLOCK_OFFSET(start_nid); - - if (f2fs_test_bit(block_off, nm_i->nat_bitmap)) - f2fs_clear_bit(block_off, nm_i->nat_bitmap); - else - f2fs_set_bit(block_off, nm_i->nat_bitmap); -} - -static inline void fill_node_footer(struct page *page, nid_t nid, - nid_t ino, unsigned int ofs, bool reset) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - if (reset) - memset(rn, 0, sizeof(*rn)); - rn->footer.nid = cpu_to_le32(nid); - rn->footer.ino = cpu_to_le32(ino); - rn->footer.flag = cpu_to_le32(ofs << OFFSET_BIT_SHIFT); -} - -static inline void copy_node_footer(struct page *dst, struct page *src) -{ - void *src_addr = page_address(src); - void *dst_addr = page_address(dst); - struct f2fs_node *src_rn = (struct f2fs_node *)src_addr; - struct f2fs_node *dst_rn = (struct f2fs_node *)dst_addr; - memcpy(&dst_rn->footer, &src_rn->footer, sizeof(struct node_footer)); -} - -static inline void fill_node_footer_blkaddr(struct page *page, block_t blkaddr) -{ - struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - rn->footer.cp_ver = ckpt->checkpoint_ver; - rn->footer.next_blkaddr = cpu_to_le32(blkaddr); -} - -static inline nid_t ino_of_node(struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - return le32_to_cpu(rn->footer.ino); -} - -static inline nid_t nid_of_node(struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - return le32_to_cpu(rn->footer.nid); -} - -static inline unsigned int ofs_of_node(struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned flag = le32_to_cpu(rn->footer.flag); - return flag >> OFFSET_BIT_SHIFT; -} - -static inline unsigned long long cpver_of_node(struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - return le64_to_cpu(rn->footer.cp_ver); -} - -static inline block_t next_blkaddr_of_node(struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - return le32_to_cpu(rn->footer.next_blkaddr); -} - -/* - * f2fs assigns the following node offsets described as (num). - * N = NIDS_PER_BLOCK - * - * Inode block (0) - * |- direct node (1) - * |- direct node (2) - * |- indirect node (3) - * | `- direct node (4 => 4 + N - 1) - * |- indirect node (4 + N) - * | `- direct node (5 + N => 5 + 2N - 1) - * `- double indirect node (5 + 2N) - * `- indirect node (6 + 2N) - * `- direct node (x(N + 1)) - */ -static inline bool IS_DNODE(struct page *node_page) -{ - unsigned int ofs = ofs_of_node(node_page); - if (ofs == 3 || ofs == 4 + NIDS_PER_BLOCK || - ofs == 5 + 2 * NIDS_PER_BLOCK) - return false; - if (ofs >= 6 + 2 * NIDS_PER_BLOCK) { - ofs -= 6 + 2 * NIDS_PER_BLOCK; - if ((long int)ofs % (NIDS_PER_BLOCK + 1)) - return false; - } - return true; -} - -static inline void set_nid(struct page *p, int off, nid_t nid, bool i) -{ - struct f2fs_node *rn = (struct f2fs_node *)page_address(p); - - wait_on_page_writeback(p); - - if (i) - rn->i.i_nid[off - NODE_DIR1_BLOCK] = cpu_to_le32(nid); - else - rn->in.nid[off] = cpu_to_le32(nid); - set_page_dirty(p); -} - -static inline nid_t get_nid(struct page *p, int off, bool i) -{ - struct f2fs_node *rn = (struct f2fs_node *)page_address(p); - if (i) - return le32_to_cpu(rn->i.i_nid[off - NODE_DIR1_BLOCK]); - return le32_to_cpu(rn->in.nid[off]); -} - -/* - * Coldness identification: - * - Mark cold files in f2fs_inode_info - * - Mark cold node blocks in their node footer - * - Mark cold data pages in page cache - */ -static inline int is_cold_file(struct inode *inode) -{ - return F2FS_I(inode)->i_advise & FADVISE_COLD_BIT; -} - -static inline int is_cold_data(struct page *page) -{ - return PageChecked(page); -} - -static inline void set_cold_data(struct page *page) -{ - SetPageChecked(page); -} - -static inline void clear_cold_data(struct page *page) -{ - ClearPageChecked(page); -} - -static inline int is_cold_node(struct page *page) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned int flag = le32_to_cpu(rn->footer.flag); - return flag & (0x1 << COLD_BIT_SHIFT); -} - -static inline unsigned char is_fsync_dnode(struct page *page) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned int flag = le32_to_cpu(rn->footer.flag); - return flag & (0x1 << FSYNC_BIT_SHIFT); -} - -static inline unsigned char is_dent_dnode(struct page *page) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned int flag = le32_to_cpu(rn->footer.flag); - return flag & (0x1 << DENT_BIT_SHIFT); -} - -static inline void set_cold_node(struct inode *inode, struct page *page) -{ - struct f2fs_node *rn = (struct f2fs_node *)page_address(page); - unsigned int flag = le32_to_cpu(rn->footer.flag); - - if (S_ISDIR(inode->i_mode)) - flag &= ~(0x1 << COLD_BIT_SHIFT); - else - flag |= (0x1 << COLD_BIT_SHIFT); - rn->footer.flag = cpu_to_le32(flag); -} - -static inline void set_fsync_mark(struct page *page, int mark) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned int flag = le32_to_cpu(rn->footer.flag); - if (mark) - flag |= (0x1 << FSYNC_BIT_SHIFT); - else - flag &= ~(0x1 << FSYNC_BIT_SHIFT); - rn->footer.flag = cpu_to_le32(flag); -} - -static inline void set_dentry_mark(struct page *page, int mark) -{ - void *kaddr = page_address(page); - struct f2fs_node *rn = (struct f2fs_node *)kaddr; - unsigned int flag = le32_to_cpu(rn->footer.flag); - if (mark) - flag |= (0x1 << DENT_BIT_SHIFT); - else - flag &= ~(0x1 << DENT_BIT_SHIFT); - rn->footer.flag = cpu_to_le32(flag); -} diff --git a/trunk/fs/f2fs/recovery.c b/trunk/fs/f2fs/recovery.c deleted file mode 100644 index b07e9b6ef376..000000000000 --- a/trunk/fs/f2fs/recovery.c +++ /dev/null @@ -1,375 +0,0 @@ -/* - * fs/f2fs/recovery.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include "f2fs.h" -#include "node.h" -#include "segment.h" - -static struct kmem_cache *fsync_entry_slab; - -bool space_for_roll_forward(struct f2fs_sb_info *sbi) -{ - if (sbi->last_valid_block_count + sbi->alloc_valid_block_count - > sbi->user_block_count) - return false; - return true; -} - -static struct fsync_inode_entry *get_fsync_inode(struct list_head *head, - nid_t ino) -{ - struct list_head *this; - struct fsync_inode_entry *entry; - - list_for_each(this, head) { - entry = list_entry(this, struct fsync_inode_entry, list); - if (entry->inode->i_ino == ino) - return entry; - } - return NULL; -} - -static int recover_dentry(struct page *ipage, struct inode *inode) -{ - struct f2fs_node *raw_node = (struct f2fs_node *)kmap(ipage); - struct f2fs_inode *raw_inode = &(raw_node->i); - struct dentry dent, parent; - struct f2fs_dir_entry *de; - struct page *page; - struct inode *dir; - int err = 0; - - if (!is_dent_dnode(ipage)) - goto out; - - dir = f2fs_iget(inode->i_sb, le32_to_cpu(raw_inode->i_pino)); - if (IS_ERR(dir)) { - err = -EINVAL; - goto out; - } - - parent.d_inode = dir; - dent.d_parent = &parent; - dent.d_name.len = le32_to_cpu(raw_inode->i_namelen); - dent.d_name.name = raw_inode->i_name; - - de = f2fs_find_entry(dir, &dent.d_name, &page); - if (de) { - kunmap(page); - f2fs_put_page(page, 0); - } else { - f2fs_add_link(&dent, inode); - } - iput(dir); -out: - kunmap(ipage); - return err; -} - -static int recover_inode(struct inode *inode, struct page *node_page) -{ - void *kaddr = page_address(node_page); - struct f2fs_node *raw_node = (struct f2fs_node *)kaddr; - struct f2fs_inode *raw_inode = &(raw_node->i); - - inode->i_mode = le16_to_cpu(raw_inode->i_mode); - i_size_write(inode, le64_to_cpu(raw_inode->i_size)); - inode->i_atime.tv_sec = le64_to_cpu(raw_inode->i_mtime); - inode->i_ctime.tv_sec = le64_to_cpu(raw_inode->i_ctime); - inode->i_mtime.tv_sec = le64_to_cpu(raw_inode->i_mtime); - inode->i_atime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); - inode->i_ctime.tv_nsec = le32_to_cpu(raw_inode->i_ctime_nsec); - inode->i_mtime.tv_nsec = le32_to_cpu(raw_inode->i_mtime_nsec); - - return recover_dentry(node_page, inode); -} - -static int find_fsync_dnodes(struct f2fs_sb_info *sbi, struct list_head *head) -{ - unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver); - struct curseg_info *curseg; - struct page *page; - block_t blkaddr; - int err = 0; - - /* get node pages in the current segment */ - curseg = CURSEG_I(sbi, CURSEG_WARM_NODE); - blkaddr = START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff; - - /* read node page */ - page = alloc_page(GFP_F2FS_ZERO); - if (IS_ERR(page)) - return PTR_ERR(page); - lock_page(page); - - while (1) { - struct fsync_inode_entry *entry; - - if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) - goto out; - - if (cp_ver != cpver_of_node(page)) - goto out; - - if (!is_fsync_dnode(page)) - goto next; - - entry = get_fsync_inode(head, ino_of_node(page)); - if (entry) { - entry->blkaddr = blkaddr; - if (IS_INODE(page) && is_dent_dnode(page)) - set_inode_flag(F2FS_I(entry->inode), - FI_INC_LINK); - } else { - if (IS_INODE(page) && is_dent_dnode(page)) { - if (recover_inode_page(sbi, page)) { - err = -ENOMEM; - goto out; - } - } - - /* add this fsync inode to the list */ - entry = kmem_cache_alloc(fsync_entry_slab, GFP_NOFS); - if (!entry) { - err = -ENOMEM; - goto out; - } - - INIT_LIST_HEAD(&entry->list); - list_add_tail(&entry->list, head); - - entry->inode = f2fs_iget(sbi->sb, ino_of_node(page)); - if (IS_ERR(entry->inode)) { - err = PTR_ERR(entry->inode); - goto out; - } - entry->blkaddr = blkaddr; - } - if (IS_INODE(page)) { - err = recover_inode(entry->inode, page); - if (err) - goto out; - } -next: - /* check next segment */ - blkaddr = next_blkaddr_of_node(page); - ClearPageUptodate(page); - } -out: - unlock_page(page); - __free_pages(page, 0); - return err; -} - -static void destroy_fsync_dnodes(struct f2fs_sb_info *sbi, - struct list_head *head) -{ - struct list_head *this; - struct fsync_inode_entry *entry; - list_for_each(this, head) { - entry = list_entry(this, struct fsync_inode_entry, list); - iput(entry->inode); - list_del(&entry->list); - kmem_cache_free(fsync_entry_slab, entry); - } -} - -static void check_index_in_prev_nodes(struct f2fs_sb_info *sbi, - block_t blkaddr) -{ - struct seg_entry *sentry; - unsigned int segno = GET_SEGNO(sbi, blkaddr); - unsigned short blkoff = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) & - (sbi->blocks_per_seg - 1); - struct f2fs_summary sum; - nid_t ino; - void *kaddr; - struct inode *inode; - struct page *node_page; - block_t bidx; - int i; - - sentry = get_seg_entry(sbi, segno); - if (!f2fs_test_bit(blkoff, sentry->cur_valid_map)) - return; - - /* Get the previous summary */ - for (i = CURSEG_WARM_DATA; i <= CURSEG_COLD_DATA; i++) { - struct curseg_info *curseg = CURSEG_I(sbi, i); - if (curseg->segno == segno) { - sum = curseg->sum_blk->entries[blkoff]; - break; - } - } - if (i > CURSEG_COLD_DATA) { - struct page *sum_page = get_sum_page(sbi, segno); - struct f2fs_summary_block *sum_node; - kaddr = page_address(sum_page); - sum_node = (struct f2fs_summary_block *)kaddr; - sum = sum_node->entries[blkoff]; - f2fs_put_page(sum_page, 1); - } - - /* Get the node page */ - node_page = get_node_page(sbi, le32_to_cpu(sum.nid)); - bidx = start_bidx_of_node(ofs_of_node(node_page)) + - le16_to_cpu(sum.ofs_in_node); - ino = ino_of_node(node_page); - f2fs_put_page(node_page, 1); - - /* Deallocate previous index in the node page */ - inode = f2fs_iget_nowait(sbi->sb, ino); - truncate_hole(inode, bidx, bidx + 1); - iput(inode); -} - -static void do_recover_data(struct f2fs_sb_info *sbi, struct inode *inode, - struct page *page, block_t blkaddr) -{ - unsigned int start, end; - struct dnode_of_data dn; - struct f2fs_summary sum; - struct node_info ni; - - start = start_bidx_of_node(ofs_of_node(page)); - if (IS_INODE(page)) - end = start + ADDRS_PER_INODE; - else - end = start + ADDRS_PER_BLOCK; - - set_new_dnode(&dn, inode, NULL, NULL, 0); - if (get_dnode_of_data(&dn, start, 0)) - return; - - wait_on_page_writeback(dn.node_page); - - get_node_info(sbi, dn.nid, &ni); - BUG_ON(ni.ino != ino_of_node(page)); - BUG_ON(ofs_of_node(dn.node_page) != ofs_of_node(page)); - - for (; start < end; start++) { - block_t src, dest; - - src = datablock_addr(dn.node_page, dn.ofs_in_node); - dest = datablock_addr(page, dn.ofs_in_node); - - if (src != dest && dest != NEW_ADDR && dest != NULL_ADDR) { - if (src == NULL_ADDR) { - int err = reserve_new_block(&dn); - /* We should not get -ENOSPC */ - BUG_ON(err); - } - - /* Check the previous node page having this index */ - check_index_in_prev_nodes(sbi, dest); - - set_summary(&sum, dn.nid, dn.ofs_in_node, ni.version); - - /* write dummy data page */ - recover_data_page(sbi, NULL, &sum, src, dest); - update_extent_cache(dest, &dn); - } - dn.ofs_in_node++; - } - - /* write node page in place */ - set_summary(&sum, dn.nid, 0, 0); - if (IS_INODE(dn.node_page)) - sync_inode_page(&dn); - - copy_node_footer(dn.node_page, page); - fill_node_footer(dn.node_page, dn.nid, ni.ino, - ofs_of_node(page), false); - set_page_dirty(dn.node_page); - - recover_node_page(sbi, dn.node_page, &sum, &ni, blkaddr); - f2fs_put_dnode(&dn); -} - -static void recover_data(struct f2fs_sb_info *sbi, - struct list_head *head, int type) -{ - unsigned long long cp_ver = le64_to_cpu(sbi->ckpt->checkpoint_ver); - struct curseg_info *curseg; - struct page *page; - block_t blkaddr; - - /* get node pages in the current segment */ - curseg = CURSEG_I(sbi, type); - blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); - - /* read node page */ - page = alloc_page(GFP_NOFS | __GFP_ZERO); - if (IS_ERR(page)) - return; - lock_page(page); - - while (1) { - struct fsync_inode_entry *entry; - - if (f2fs_readpage(sbi, page, blkaddr, READ_SYNC)) - goto out; - - if (cp_ver != cpver_of_node(page)) - goto out; - - entry = get_fsync_inode(head, ino_of_node(page)); - if (!entry) - goto next; - - do_recover_data(sbi, entry->inode, page, blkaddr); - - if (entry->blkaddr == blkaddr) { - iput(entry->inode); - list_del(&entry->list); - kmem_cache_free(fsync_entry_slab, entry); - } -next: - /* check next segment */ - blkaddr = next_blkaddr_of_node(page); - ClearPageUptodate(page); - } -out: - unlock_page(page); - __free_pages(page, 0); - - allocate_new_segments(sbi); -} - -void recover_fsync_data(struct f2fs_sb_info *sbi) -{ - struct list_head inode_list; - - fsync_entry_slab = f2fs_kmem_cache_create("f2fs_fsync_inode_entry", - sizeof(struct fsync_inode_entry), NULL); - if (unlikely(!fsync_entry_slab)) - return; - - INIT_LIST_HEAD(&inode_list); - - /* step #1: find fsynced inode numbers */ - if (find_fsync_dnodes(sbi, &inode_list)) - goto out; - - if (list_empty(&inode_list)) - goto out; - - /* step #2: recover data */ - sbi->por_doing = 1; - recover_data(sbi, &inode_list, CURSEG_WARM_NODE); - sbi->por_doing = 0; - BUG_ON(!list_empty(&inode_list)); -out: - destroy_fsync_dnodes(sbi, &inode_list); - kmem_cache_destroy(fsync_entry_slab); - write_checkpoint(sbi, false, false); -} diff --git a/trunk/fs/f2fs/segment.c b/trunk/fs/f2fs/segment.c deleted file mode 100644 index 1b26e4ea1016..000000000000 --- a/trunk/fs/f2fs/segment.c +++ /dev/null @@ -1,1791 +0,0 @@ -/* - * fs/f2fs/segment.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "segment.h" -#include "node.h" - -static int need_to_flush(struct f2fs_sb_info *sbi) -{ - unsigned int pages_per_sec = (1 << sbi->log_blocks_per_seg) * - sbi->segs_per_sec; - int node_secs = ((get_pages(sbi, F2FS_DIRTY_NODES) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - int dent_secs = ((get_pages(sbi, F2FS_DIRTY_DENTS) + pages_per_sec - 1) - >> sbi->log_blocks_per_seg) / sbi->segs_per_sec; - - if (sbi->por_doing) - return 0; - - if (free_sections(sbi) <= (node_secs + 2 * dent_secs + - reserved_sections(sbi))) - return 1; - return 0; -} - -/* - * This function balances dirty node and dentry pages. - * In addition, it controls garbage collection. - */ -void f2fs_balance_fs(struct f2fs_sb_info *sbi) -{ - struct writeback_control wbc = { - .sync_mode = WB_SYNC_ALL, - .nr_to_write = LONG_MAX, - .for_reclaim = 0, - }; - - if (sbi->por_doing) - return; - - /* - * We should do checkpoint when there are so many dirty node pages - * with enough free segments. After then, we should do GC. - */ - if (need_to_flush(sbi)) { - sync_dirty_dir_inodes(sbi); - sync_node_pages(sbi, 0, &wbc); - } - - if (has_not_enough_free_secs(sbi)) { - mutex_lock(&sbi->gc_mutex); - f2fs_gc(sbi, 1); - } -} - -static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, - enum dirty_type dirty_type) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - - /* need not be added */ - if (IS_CURSEG(sbi, segno)) - return; - - if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) - dirty_i->nr_dirty[dirty_type]++; - - if (dirty_type == DIRTY) { - struct seg_entry *sentry = get_seg_entry(sbi, segno); - dirty_type = sentry->type; - if (!test_and_set_bit(segno, dirty_i->dirty_segmap[dirty_type])) - dirty_i->nr_dirty[dirty_type]++; - } -} - -static void __remove_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, - enum dirty_type dirty_type) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - - if (test_and_clear_bit(segno, dirty_i->dirty_segmap[dirty_type])) - dirty_i->nr_dirty[dirty_type]--; - - if (dirty_type == DIRTY) { - struct seg_entry *sentry = get_seg_entry(sbi, segno); - dirty_type = sentry->type; - if (test_and_clear_bit(segno, - dirty_i->dirty_segmap[dirty_type])) - dirty_i->nr_dirty[dirty_type]--; - clear_bit(segno, dirty_i->victim_segmap[FG_GC]); - clear_bit(segno, dirty_i->victim_segmap[BG_GC]); - } -} - -/* - * Should not occur error such as -ENOMEM. - * Adding dirty entry into seglist is not critical operation. - * If a given segment is one of current working segments, it won't be added. - */ -void locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned short valid_blocks; - - if (segno == NULL_SEGNO || IS_CURSEG(sbi, segno)) - return; - - mutex_lock(&dirty_i->seglist_lock); - - valid_blocks = get_valid_blocks(sbi, segno, 0); - - if (valid_blocks == 0) { - __locate_dirty_segment(sbi, segno, PRE); - __remove_dirty_segment(sbi, segno, DIRTY); - } else if (valid_blocks < sbi->blocks_per_seg) { - __locate_dirty_segment(sbi, segno, DIRTY); - } else { - /* Recovery routine with SSR needs this */ - __remove_dirty_segment(sbi, segno, DIRTY); - } - - mutex_unlock(&dirty_i->seglist_lock); - return; -} - -/* - * Should call clear_prefree_segments after checkpoint is done. - */ -static void set_prefree_as_free_segments(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned int segno, offset = 0; - unsigned int total_segs = TOTAL_SEGS(sbi); - - mutex_lock(&dirty_i->seglist_lock); - while (1) { - segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs, - offset); - if (segno >= total_segs) - break; - __set_test_and_free(sbi, segno); - offset = segno + 1; - } - mutex_unlock(&dirty_i->seglist_lock); -} - -void clear_prefree_segments(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned int segno, offset = 0; - unsigned int total_segs = TOTAL_SEGS(sbi); - - mutex_lock(&dirty_i->seglist_lock); - while (1) { - segno = find_next_bit(dirty_i->dirty_segmap[PRE], total_segs, - offset); - if (segno >= total_segs) - break; - - offset = segno + 1; - if (test_and_clear_bit(segno, dirty_i->dirty_segmap[PRE])) - dirty_i->nr_dirty[PRE]--; - - /* Let's use trim */ - if (test_opt(sbi, DISCARD)) - blkdev_issue_discard(sbi->sb->s_bdev, - START_BLOCK(sbi, segno) << - sbi->log_sectors_per_block, - 1 << (sbi->log_sectors_per_block + - sbi->log_blocks_per_seg), - GFP_NOFS, 0); - } - mutex_unlock(&dirty_i->seglist_lock); -} - -static void __mark_sit_entry_dirty(struct f2fs_sb_info *sbi, unsigned int segno) -{ - struct sit_info *sit_i = SIT_I(sbi); - if (!__test_and_set_bit(segno, sit_i->dirty_sentries_bitmap)) - sit_i->dirty_sentries++; -} - -static void __set_sit_entry_type(struct f2fs_sb_info *sbi, int type, - unsigned int segno, int modified) -{ - struct seg_entry *se = get_seg_entry(sbi, segno); - se->type = type; - if (modified) - __mark_sit_entry_dirty(sbi, segno); -} - -static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del) -{ - struct seg_entry *se; - unsigned int segno, offset; - long int new_vblocks; - - segno = GET_SEGNO(sbi, blkaddr); - - se = get_seg_entry(sbi, segno); - new_vblocks = se->valid_blocks + del; - offset = GET_SEGOFF_FROM_SEG0(sbi, blkaddr) & (sbi->blocks_per_seg - 1); - - BUG_ON((new_vblocks >> (sizeof(unsigned short) << 3) || - (new_vblocks > sbi->blocks_per_seg))); - - se->valid_blocks = new_vblocks; - se->mtime = get_mtime(sbi); - SIT_I(sbi)->max_mtime = se->mtime; - - /* Update valid block bitmap */ - if (del > 0) { - if (f2fs_set_bit(offset, se->cur_valid_map)) - BUG(); - } else { - if (!f2fs_clear_bit(offset, se->cur_valid_map)) - BUG(); - } - if (!f2fs_test_bit(offset, se->ckpt_valid_map)) - se->ckpt_valid_blocks += del; - - __mark_sit_entry_dirty(sbi, segno); - - /* update total number of valid blocks to be written in ckpt area */ - SIT_I(sbi)->written_valid_blocks += del; - - if (sbi->segs_per_sec > 1) - get_sec_entry(sbi, segno)->valid_blocks += del; -} - -static void refresh_sit_entry(struct f2fs_sb_info *sbi, - block_t old_blkaddr, block_t new_blkaddr) -{ - update_sit_entry(sbi, new_blkaddr, 1); - if (GET_SEGNO(sbi, old_blkaddr) != NULL_SEGNO) - update_sit_entry(sbi, old_blkaddr, -1); -} - -void invalidate_blocks(struct f2fs_sb_info *sbi, block_t addr) -{ - unsigned int segno = GET_SEGNO(sbi, addr); - struct sit_info *sit_i = SIT_I(sbi); - - BUG_ON(addr == NULL_ADDR); - if (addr == NEW_ADDR) - return; - - /* add it into sit main buffer */ - mutex_lock(&sit_i->sentry_lock); - - update_sit_entry(sbi, addr, -1); - - /* add it into dirty seglist */ - locate_dirty_segment(sbi, segno); - - mutex_unlock(&sit_i->sentry_lock); -} - -/* - * This function should be resided under the curseg_mutex lock - */ -static void __add_sum_entry(struct f2fs_sb_info *sbi, int type, - struct f2fs_summary *sum, unsigned short offset) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - void *addr = curseg->sum_blk; - addr += offset * sizeof(struct f2fs_summary); - memcpy(addr, sum, sizeof(struct f2fs_summary)); - return; -} - -/* - * Calculate the number of current summary pages for writing - */ -int npages_for_summary_flush(struct f2fs_sb_info *sbi) -{ - int total_size_bytes = 0; - int valid_sum_count = 0; - int i, sum_space; - - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { - if (sbi->ckpt->alloc_type[i] == SSR) - valid_sum_count += sbi->blocks_per_seg; - else - valid_sum_count += curseg_blkoff(sbi, i); - } - - total_size_bytes = valid_sum_count * (SUMMARY_SIZE + 1) - + sizeof(struct nat_journal) + 2 - + sizeof(struct sit_journal) + 2; - sum_space = PAGE_CACHE_SIZE - SUM_FOOTER_SIZE; - if (total_size_bytes < sum_space) - return 1; - else if (total_size_bytes < 2 * sum_space) - return 2; - return 3; -} - -/* - * Caller should put this summary page - */ -struct page *get_sum_page(struct f2fs_sb_info *sbi, unsigned int segno) -{ - return get_meta_page(sbi, GET_SUM_BLOCK(sbi, segno)); -} - -static void write_sum_page(struct f2fs_sb_info *sbi, - struct f2fs_summary_block *sum_blk, block_t blk_addr) -{ - struct page *page = grab_meta_page(sbi, blk_addr); - void *kaddr = page_address(page); - memcpy(kaddr, sum_blk, PAGE_CACHE_SIZE); - set_page_dirty(page); - f2fs_put_page(page, 1); -} - -static unsigned int check_prefree_segments(struct f2fs_sb_info *sbi, - int ofs_unit, int type) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned long *prefree_segmap = dirty_i->dirty_segmap[PRE]; - unsigned int segno, next_segno, i; - int ofs = 0; - - /* - * If there is not enough reserved sections, - * we should not reuse prefree segments. - */ - if (has_not_enough_free_secs(sbi)) - return NULL_SEGNO; - - /* - * NODE page should not reuse prefree segment, - * since those information is used for SPOR. - */ - if (IS_NODESEG(type)) - return NULL_SEGNO; -next: - segno = find_next_bit(prefree_segmap, TOTAL_SEGS(sbi), ofs++); - ofs = ((segno / ofs_unit) * ofs_unit) + ofs_unit; - if (segno < TOTAL_SEGS(sbi)) { - /* skip intermediate segments in a section */ - if (segno % ofs_unit) - goto next; - - /* skip if whole section is not prefree */ - next_segno = find_next_zero_bit(prefree_segmap, - TOTAL_SEGS(sbi), segno + 1); - if (next_segno - segno < ofs_unit) - goto next; - - /* skip if whole section was not free at the last checkpoint */ - for (i = 0; i < ofs_unit; i++) - if (get_seg_entry(sbi, segno)->ckpt_valid_blocks) - goto next; - return segno; - } - return NULL_SEGNO; -} - -/* - * Find a new segment from the free segments bitmap to right order - * This function should be returned with success, otherwise BUG - */ -static void get_new_segment(struct f2fs_sb_info *sbi, - unsigned int *newseg, bool new_sec, int dir) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int total_secs = sbi->total_sections; - unsigned int segno, secno, zoneno; - unsigned int total_zones = sbi->total_sections / sbi->secs_per_zone; - unsigned int hint = *newseg / sbi->segs_per_sec; - unsigned int old_zoneno = GET_ZONENO_FROM_SEGNO(sbi, *newseg); - unsigned int left_start = hint; - bool init = true; - int go_left = 0; - int i; - - write_lock(&free_i->segmap_lock); - - if (!new_sec && ((*newseg + 1) % sbi->segs_per_sec)) { - segno = find_next_zero_bit(free_i->free_segmap, - TOTAL_SEGS(sbi), *newseg + 1); - if (segno < TOTAL_SEGS(sbi)) - goto got_it; - } -find_other_zone: - secno = find_next_zero_bit(free_i->free_secmap, total_secs, hint); - if (secno >= total_secs) { - if (dir == ALLOC_RIGHT) { - secno = find_next_zero_bit(free_i->free_secmap, - total_secs, 0); - BUG_ON(secno >= total_secs); - } else { - go_left = 1; - left_start = hint - 1; - } - } - if (go_left == 0) - goto skip_left; - - while (test_bit(left_start, free_i->free_secmap)) { - if (left_start > 0) { - left_start--; - continue; - } - left_start = find_next_zero_bit(free_i->free_secmap, - total_secs, 0); - BUG_ON(left_start >= total_secs); - break; - } - secno = left_start; -skip_left: - hint = secno; - segno = secno * sbi->segs_per_sec; - zoneno = secno / sbi->secs_per_zone; - - /* give up on finding another zone */ - if (!init) - goto got_it; - if (sbi->secs_per_zone == 1) - goto got_it; - if (zoneno == old_zoneno) - goto got_it; - if (dir == ALLOC_LEFT) { - if (!go_left && zoneno + 1 >= total_zones) - goto got_it; - if (go_left && zoneno == 0) - goto got_it; - } - for (i = 0; i < NR_CURSEG_TYPE; i++) - if (CURSEG_I(sbi, i)->zone == zoneno) - break; - - if (i < NR_CURSEG_TYPE) { - /* zone is in user, try another */ - if (go_left) - hint = zoneno * sbi->secs_per_zone - 1; - else if (zoneno + 1 >= total_zones) - hint = 0; - else - hint = (zoneno + 1) * sbi->secs_per_zone; - init = false; - goto find_other_zone; - } -got_it: - /* set it as dirty segment in free segmap */ - BUG_ON(test_bit(segno, free_i->free_segmap)); - __set_inuse(sbi, segno); - *newseg = segno; - write_unlock(&free_i->segmap_lock); -} - -static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - struct summary_footer *sum_footer; - - curseg->segno = curseg->next_segno; - curseg->zone = GET_ZONENO_FROM_SEGNO(sbi, curseg->segno); - curseg->next_blkoff = 0; - curseg->next_segno = NULL_SEGNO; - - sum_footer = &(curseg->sum_blk->footer); - memset(sum_footer, 0, sizeof(struct summary_footer)); - if (IS_DATASEG(type)) - SET_SUM_TYPE(sum_footer, SUM_TYPE_DATA); - if (IS_NODESEG(type)) - SET_SUM_TYPE(sum_footer, SUM_TYPE_NODE); - __set_sit_entry_type(sbi, type, curseg->segno, modified); -} - -/* - * Allocate a current working segment. - * This function always allocates a free segment in LFS manner. - */ -static void new_curseg(struct f2fs_sb_info *sbi, int type, bool new_sec) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - unsigned int segno = curseg->segno; - int dir = ALLOC_LEFT; - - write_sum_page(sbi, curseg->sum_blk, - GET_SUM_BLOCK(sbi, curseg->segno)); - if (type == CURSEG_WARM_DATA || type == CURSEG_COLD_DATA) - dir = ALLOC_RIGHT; - - if (test_opt(sbi, NOHEAP)) - dir = ALLOC_RIGHT; - - get_new_segment(sbi, &segno, new_sec, dir); - curseg->next_segno = segno; - reset_curseg(sbi, type, 1); - curseg->alloc_type = LFS; -} - -static void __next_free_blkoff(struct f2fs_sb_info *sbi, - struct curseg_info *seg, block_t start) -{ - struct seg_entry *se = get_seg_entry(sbi, seg->segno); - block_t ofs; - for (ofs = start; ofs < sbi->blocks_per_seg; ofs++) { - if (!f2fs_test_bit(ofs, se->ckpt_valid_map) - && !f2fs_test_bit(ofs, se->cur_valid_map)) - break; - } - seg->next_blkoff = ofs; -} - -/* - * If a segment is written by LFS manner, next block offset is just obtained - * by increasing the current block offset. However, if a segment is written by - * SSR manner, next block offset obtained by calling __next_free_blkoff - */ -static void __refresh_next_blkoff(struct f2fs_sb_info *sbi, - struct curseg_info *seg) -{ - if (seg->alloc_type == SSR) - __next_free_blkoff(sbi, seg, seg->next_blkoff + 1); - else - seg->next_blkoff++; -} - -/* - * This function always allocates a used segment (from dirty seglist) by SSR - * manner, so it should recover the existing segment information of valid blocks - */ -static void change_curseg(struct f2fs_sb_info *sbi, int type, bool reuse) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, type); - unsigned int new_segno = curseg->next_segno; - struct f2fs_summary_block *sum_node; - struct page *sum_page; - - write_sum_page(sbi, curseg->sum_blk, - GET_SUM_BLOCK(sbi, curseg->segno)); - __set_test_and_inuse(sbi, new_segno); - - mutex_lock(&dirty_i->seglist_lock); - __remove_dirty_segment(sbi, new_segno, PRE); - __remove_dirty_segment(sbi, new_segno, DIRTY); - mutex_unlock(&dirty_i->seglist_lock); - - reset_curseg(sbi, type, 1); - curseg->alloc_type = SSR; - __next_free_blkoff(sbi, curseg, 0); - - if (reuse) { - sum_page = get_sum_page(sbi, new_segno); - sum_node = (struct f2fs_summary_block *)page_address(sum_page); - memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); - f2fs_put_page(sum_page, 1); - } -} - -/* - * flush out current segment and replace it with new segment - * This function should be returned with success, otherwise BUG - */ -static void allocate_segment_by_default(struct f2fs_sb_info *sbi, - int type, bool force) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - unsigned int ofs_unit; - - if (force) { - new_curseg(sbi, type, true); - goto out; - } - - ofs_unit = need_SSR(sbi) ? 1 : sbi->segs_per_sec; - curseg->next_segno = check_prefree_segments(sbi, ofs_unit, type); - - if (curseg->next_segno != NULL_SEGNO) - change_curseg(sbi, type, false); - else if (type == CURSEG_WARM_NODE) - new_curseg(sbi, type, false); - else if (need_SSR(sbi) && get_ssr_segment(sbi, type)) - change_curseg(sbi, type, true); - else - new_curseg(sbi, type, false); -out: - sbi->segment_count[curseg->alloc_type]++; -} - -void allocate_new_segments(struct f2fs_sb_info *sbi) -{ - struct curseg_info *curseg; - unsigned int old_curseg; - int i; - - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { - curseg = CURSEG_I(sbi, i); - old_curseg = curseg->segno; - SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); - locate_dirty_segment(sbi, old_curseg); - } -} - -static const struct segment_allocation default_salloc_ops = { - .allocate_segment = allocate_segment_by_default, -}; - -static void f2fs_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; - struct bio_private *p = bio->bi_private; - - 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); - set_ckpt_flags(p->sbi->ckpt, CP_ERROR_FLAG); - set_page_dirty(page); - } - end_page_writeback(page); - dec_page_count(p->sbi, F2FS_WRITEBACK); - } while (bvec >= bio->bi_io_vec); - - if (p->is_sync) - complete(p->wait); - kfree(p); - bio_put(bio); -} - -struct bio *f2fs_bio_alloc(struct block_device *bdev, int npages) -{ - struct bio *bio; - struct bio_private *priv; -retry: - priv = kmalloc(sizeof(struct bio_private), GFP_NOFS); - if (!priv) { - cond_resched(); - goto retry; - } - - /* No failure on bio allocation */ - bio = bio_alloc(GFP_NOIO, npages); - bio->bi_bdev = bdev; - bio->bi_private = priv; - return bio; -} - -static void do_submit_bio(struct f2fs_sb_info *sbi, - enum page_type type, bool sync) -{ - int rw = sync ? WRITE_SYNC : WRITE; - enum page_type btype = type > META ? META : type; - - if (type >= META_FLUSH) - rw = WRITE_FLUSH_FUA; - - if (sbi->bio[btype]) { - struct bio_private *p = sbi->bio[btype]->bi_private; - p->sbi = sbi; - sbi->bio[btype]->bi_end_io = f2fs_end_io_write; - if (type == META_FLUSH) { - DECLARE_COMPLETION_ONSTACK(wait); - p->is_sync = true; - p->wait = &wait; - submit_bio(rw, sbi->bio[btype]); - wait_for_completion(&wait); - } else { - p->is_sync = false; - submit_bio(rw, sbi->bio[btype]); - } - sbi->bio[btype] = NULL; - } -} - -void f2fs_submit_bio(struct f2fs_sb_info *sbi, enum page_type type, bool sync) -{ - down_write(&sbi->bio_sem); - do_submit_bio(sbi, type, sync); - up_write(&sbi->bio_sem); -} - -static void submit_write_page(struct f2fs_sb_info *sbi, struct page *page, - block_t blk_addr, enum page_type type) -{ - struct block_device *bdev = sbi->sb->s_bdev; - - verify_block_addr(sbi, blk_addr); - - down_write(&sbi->bio_sem); - - inc_page_count(sbi, F2FS_WRITEBACK); - - if (sbi->bio[type] && sbi->last_block_in_bio[type] != blk_addr - 1) - do_submit_bio(sbi, type, false); -alloc_new: - if (sbi->bio[type] == NULL) { - sbi->bio[type] = f2fs_bio_alloc(bdev, bio_get_nr_vecs(bdev)); - sbi->bio[type]->bi_sector = SECTOR_FROM_BLOCK(sbi, blk_addr); - /* - * The end_io will be assigned at the sumbission phase. - * Until then, let bio_add_page() merge consecutive IOs as much - * as possible. - */ - } - - if (bio_add_page(sbi->bio[type], page, PAGE_CACHE_SIZE, 0) < - PAGE_CACHE_SIZE) { - do_submit_bio(sbi, type, false); - goto alloc_new; - } - - sbi->last_block_in_bio[type] = blk_addr; - - up_write(&sbi->bio_sem); -} - -static bool __has_curseg_space(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - if (curseg->next_blkoff < sbi->blocks_per_seg) - return true; - return false; -} - -static int __get_segment_type_2(struct page *page, enum page_type p_type) -{ - if (p_type == DATA) - return CURSEG_HOT_DATA; - else - return CURSEG_HOT_NODE; -} - -static int __get_segment_type_4(struct page *page, enum page_type p_type) -{ - if (p_type == DATA) { - struct inode *inode = page->mapping->host; - - if (S_ISDIR(inode->i_mode)) - return CURSEG_HOT_DATA; - else - return CURSEG_COLD_DATA; - } else { - if (IS_DNODE(page) && !is_cold_node(page)) - return CURSEG_HOT_NODE; - else - return CURSEG_COLD_NODE; - } -} - -static int __get_segment_type_6(struct page *page, enum page_type p_type) -{ - if (p_type == DATA) { - struct inode *inode = page->mapping->host; - - if (S_ISDIR(inode->i_mode)) - return CURSEG_HOT_DATA; - else if (is_cold_data(page) || is_cold_file(inode)) - return CURSEG_COLD_DATA; - else - return CURSEG_WARM_DATA; - } else { - if (IS_DNODE(page)) - return is_cold_node(page) ? CURSEG_WARM_NODE : - CURSEG_HOT_NODE; - else - return CURSEG_COLD_NODE; - } -} - -static int __get_segment_type(struct page *page, enum page_type p_type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(page->mapping->host->i_sb); - switch (sbi->active_logs) { - case 2: - return __get_segment_type_2(page, p_type); - case 4: - return __get_segment_type_4(page, p_type); - case 6: - return __get_segment_type_6(page, p_type); - default: - BUG(); - } -} - -static void do_write_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blkaddr, block_t *new_blkaddr, - struct f2fs_summary *sum, enum page_type p_type) -{ - struct sit_info *sit_i = SIT_I(sbi); - struct curseg_info *curseg; - unsigned int old_cursegno; - int type; - - type = __get_segment_type(page, p_type); - curseg = CURSEG_I(sbi, type); - - mutex_lock(&curseg->curseg_mutex); - - *new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg); - old_cursegno = curseg->segno; - - /* - * __add_sum_entry should be resided under the curseg_mutex - * because, this function updates a summary entry in the - * current summary block. - */ - __add_sum_entry(sbi, type, sum, curseg->next_blkoff); - - mutex_lock(&sit_i->sentry_lock); - __refresh_next_blkoff(sbi, curseg); - sbi->block_count[curseg->alloc_type]++; - - /* - * SIT information should be updated before segment allocation, - * since SSR needs latest valid block information. - */ - refresh_sit_entry(sbi, old_blkaddr, *new_blkaddr); - - if (!__has_curseg_space(sbi, type)) - sit_i->s_ops->allocate_segment(sbi, type, false); - - locate_dirty_segment(sbi, old_cursegno); - locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); - mutex_unlock(&sit_i->sentry_lock); - - if (p_type == NODE) - fill_node_footer_blkaddr(page, NEXT_FREE_BLKADDR(sbi, curseg)); - - /* writeout dirty page into bdev */ - submit_write_page(sbi, page, *new_blkaddr, p_type); - - mutex_unlock(&curseg->curseg_mutex); -} - -int write_meta_page(struct f2fs_sb_info *sbi, struct page *page, - struct writeback_control *wbc) -{ - if (wbc->for_reclaim) - return AOP_WRITEPAGE_ACTIVATE; - - set_page_writeback(page); - submit_write_page(sbi, page, page->index, META); - return 0; -} - -void write_node_page(struct f2fs_sb_info *sbi, struct page *page, - unsigned int nid, block_t old_blkaddr, block_t *new_blkaddr) -{ - struct f2fs_summary sum; - set_summary(&sum, nid, 0, 0); - do_write_page(sbi, page, old_blkaddr, new_blkaddr, &sum, NODE); -} - -void write_data_page(struct inode *inode, struct page *page, - struct dnode_of_data *dn, block_t old_blkaddr, - block_t *new_blkaddr) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_summary sum; - struct node_info ni; - - BUG_ON(old_blkaddr == NULL_ADDR); - get_node_info(sbi, dn->nid, &ni); - set_summary(&sum, dn->nid, dn->ofs_in_node, ni.version); - - do_write_page(sbi, page, old_blkaddr, - new_blkaddr, &sum, DATA); -} - -void rewrite_data_page(struct f2fs_sb_info *sbi, struct page *page, - block_t old_blk_addr) -{ - submit_write_page(sbi, page, old_blk_addr, DATA); -} - -void recover_data_page(struct f2fs_sb_info *sbi, - struct page *page, struct f2fs_summary *sum, - block_t old_blkaddr, block_t new_blkaddr) -{ - struct sit_info *sit_i = SIT_I(sbi); - struct curseg_info *curseg; - unsigned int segno, old_cursegno; - struct seg_entry *se; - int type; - - segno = GET_SEGNO(sbi, new_blkaddr); - se = get_seg_entry(sbi, segno); - type = se->type; - - if (se->valid_blocks == 0 && !IS_CURSEG(sbi, segno)) { - if (old_blkaddr == NULL_ADDR) - type = CURSEG_COLD_DATA; - else - type = CURSEG_WARM_DATA; - } - curseg = CURSEG_I(sbi, type); - - mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); - - old_cursegno = curseg->segno; - - /* change the current segment */ - if (segno != curseg->segno) { - curseg->next_segno = segno; - change_curseg(sbi, type, true); - } - - curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) & - (sbi->blocks_per_seg - 1); - __add_sum_entry(sbi, type, sum, curseg->next_blkoff); - - refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); - - locate_dirty_segment(sbi, old_cursegno); - locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); - - mutex_unlock(&sit_i->sentry_lock); - mutex_unlock(&curseg->curseg_mutex); -} - -void rewrite_node_page(struct f2fs_sb_info *sbi, - struct page *page, struct f2fs_summary *sum, - block_t old_blkaddr, block_t new_blkaddr) -{ - struct sit_info *sit_i = SIT_I(sbi); - int type = CURSEG_WARM_NODE; - struct curseg_info *curseg; - unsigned int segno, old_cursegno; - block_t next_blkaddr = next_blkaddr_of_node(page); - unsigned int next_segno = GET_SEGNO(sbi, next_blkaddr); - - curseg = CURSEG_I(sbi, type); - - mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); - - segno = GET_SEGNO(sbi, new_blkaddr); - old_cursegno = curseg->segno; - - /* change the current segment */ - if (segno != curseg->segno) { - curseg->next_segno = segno; - change_curseg(sbi, type, true); - } - curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, new_blkaddr) & - (sbi->blocks_per_seg - 1); - __add_sum_entry(sbi, type, sum, curseg->next_blkoff); - - /* change the current log to the next block addr in advance */ - if (next_segno != segno) { - curseg->next_segno = next_segno; - change_curseg(sbi, type, true); - } - curseg->next_blkoff = GET_SEGOFF_FROM_SEG0(sbi, next_blkaddr) & - (sbi->blocks_per_seg - 1); - - /* rewrite node page */ - set_page_writeback(page); - submit_write_page(sbi, page, new_blkaddr, NODE); - f2fs_submit_bio(sbi, NODE, true); - refresh_sit_entry(sbi, old_blkaddr, new_blkaddr); - - locate_dirty_segment(sbi, old_cursegno); - locate_dirty_segment(sbi, GET_SEGNO(sbi, old_blkaddr)); - - mutex_unlock(&sit_i->sentry_lock); - mutex_unlock(&curseg->curseg_mutex); -} - -static int read_compacted_summaries(struct f2fs_sb_info *sbi) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - struct curseg_info *seg_i; - unsigned char *kaddr; - struct page *page; - block_t start; - int i, j, offset; - - start = start_sum_block(sbi); - - page = get_meta_page(sbi, start++); - kaddr = (unsigned char *)page_address(page); - - /* Step 1: restore nat cache */ - seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(&seg_i->sum_blk->n_nats, kaddr, SUM_JOURNAL_SIZE); - - /* Step 2: restore sit cache */ - seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(&seg_i->sum_blk->n_sits, kaddr + SUM_JOURNAL_SIZE, - SUM_JOURNAL_SIZE); - offset = 2 * SUM_JOURNAL_SIZE; - - /* Step 3: restore summary entries */ - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { - unsigned short blk_off; - unsigned int segno; - - seg_i = CURSEG_I(sbi, i); - segno = le32_to_cpu(ckpt->cur_data_segno[i]); - blk_off = le16_to_cpu(ckpt->cur_data_blkoff[i]); - seg_i->next_segno = segno; - reset_curseg(sbi, i, 0); - seg_i->alloc_type = ckpt->alloc_type[i]; - seg_i->next_blkoff = blk_off; - - if (seg_i->alloc_type == SSR) - blk_off = sbi->blocks_per_seg; - - for (j = 0; j < blk_off; j++) { - struct f2fs_summary *s; - s = (struct f2fs_summary *)(kaddr + offset); - seg_i->sum_blk->entries[j] = *s; - offset += SUMMARY_SIZE; - if (offset + SUMMARY_SIZE <= PAGE_CACHE_SIZE - - SUM_FOOTER_SIZE) - continue; - - f2fs_put_page(page, 1); - page = NULL; - - page = get_meta_page(sbi, start++); - kaddr = (unsigned char *)page_address(page); - offset = 0; - } - } - f2fs_put_page(page, 1); - return 0; -} - -static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) -{ - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - struct f2fs_summary_block *sum; - struct curseg_info *curseg; - struct page *new; - unsigned short blk_off; - unsigned int segno = 0; - block_t blk_addr = 0; - - /* get segment number and block addr */ - if (IS_DATASEG(type)) { - segno = le32_to_cpu(ckpt->cur_data_segno[type]); - blk_off = le16_to_cpu(ckpt->cur_data_blkoff[type - - CURSEG_HOT_DATA]); - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) - blk_addr = sum_blk_addr(sbi, NR_CURSEG_TYPE, type); - else - blk_addr = sum_blk_addr(sbi, NR_CURSEG_DATA_TYPE, type); - } else { - segno = le32_to_cpu(ckpt->cur_node_segno[type - - CURSEG_HOT_NODE]); - blk_off = le16_to_cpu(ckpt->cur_node_blkoff[type - - CURSEG_HOT_NODE]); - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) - blk_addr = sum_blk_addr(sbi, NR_CURSEG_NODE_TYPE, - type - CURSEG_HOT_NODE); - else - blk_addr = GET_SUM_BLOCK(sbi, segno); - } - - new = get_meta_page(sbi, blk_addr); - sum = (struct f2fs_summary_block *)page_address(new); - - if (IS_NODESEG(type)) { - if (is_set_ckpt_flags(ckpt, CP_UMOUNT_FLAG)) { - struct f2fs_summary *ns = &sum->entries[0]; - int i; - for (i = 0; i < sbi->blocks_per_seg; i++, ns++) { - ns->version = 0; - ns->ofs_in_node = 0; - } - } else { - if (restore_node_summary(sbi, segno, sum)) { - f2fs_put_page(new, 1); - return -EINVAL; - } - } - } - - /* set uncompleted segment to curseg */ - curseg = CURSEG_I(sbi, type); - mutex_lock(&curseg->curseg_mutex); - memcpy(curseg->sum_blk, sum, PAGE_CACHE_SIZE); - curseg->next_segno = segno; - reset_curseg(sbi, type, 0); - curseg->alloc_type = ckpt->alloc_type[type]; - curseg->next_blkoff = blk_off; - mutex_unlock(&curseg->curseg_mutex); - f2fs_put_page(new, 1); - return 0; -} - -static int restore_curseg_summaries(struct f2fs_sb_info *sbi) -{ - int type = CURSEG_HOT_DATA; - - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) { - /* restore for compacted data summary */ - if (read_compacted_summaries(sbi)) - return -EINVAL; - type = CURSEG_HOT_NODE; - } - - for (; type <= CURSEG_COLD_NODE; type++) - if (read_normal_summaries(sbi, type)) - return -EINVAL; - return 0; -} - -static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) -{ - struct page *page; - unsigned char *kaddr; - struct f2fs_summary *summary; - struct curseg_info *seg_i; - int written_size = 0; - int i, j; - - page = grab_meta_page(sbi, blkaddr++); - kaddr = (unsigned char *)page_address(page); - - /* Step 1: write nat cache */ - seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(kaddr, &seg_i->sum_blk->n_nats, SUM_JOURNAL_SIZE); - written_size += SUM_JOURNAL_SIZE; - - /* Step 2: write sit cache */ - seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(kaddr + written_size, &seg_i->sum_blk->n_sits, - SUM_JOURNAL_SIZE); - written_size += SUM_JOURNAL_SIZE; - - set_page_dirty(page); - - /* Step 3: write summary entries */ - for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { - unsigned short blkoff; - seg_i = CURSEG_I(sbi, i); - if (sbi->ckpt->alloc_type[i] == SSR) - blkoff = sbi->blocks_per_seg; - else - blkoff = curseg_blkoff(sbi, i); - - for (j = 0; j < blkoff; j++) { - if (!page) { - page = grab_meta_page(sbi, blkaddr++); - kaddr = (unsigned char *)page_address(page); - written_size = 0; - } - summary = (struct f2fs_summary *)(kaddr + written_size); - *summary = seg_i->sum_blk->entries[j]; - written_size += SUMMARY_SIZE; - set_page_dirty(page); - - if (written_size + SUMMARY_SIZE <= PAGE_CACHE_SIZE - - SUM_FOOTER_SIZE) - continue; - - f2fs_put_page(page, 1); - page = NULL; - } - } - if (page) - f2fs_put_page(page, 1); -} - -static void write_normal_summaries(struct f2fs_sb_info *sbi, - block_t blkaddr, int type) -{ - int i, end; - if (IS_DATASEG(type)) - end = type + NR_CURSEG_DATA_TYPE; - else - end = type + NR_CURSEG_NODE_TYPE; - - for (i = type; i < end; i++) { - struct curseg_info *sum = CURSEG_I(sbi, i); - mutex_lock(&sum->curseg_mutex); - write_sum_page(sbi, sum->sum_blk, blkaddr + (i - type)); - mutex_unlock(&sum->curseg_mutex); - } -} - -void write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk) -{ - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_COMPACT_SUM_FLAG)) - write_compacted_summaries(sbi, start_blk); - else - write_normal_summaries(sbi, start_blk, CURSEG_HOT_DATA); -} - -void write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) -{ - if (is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG)) - write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); - return; -} - -int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type, - unsigned int val, int alloc) -{ - int i; - - if (type == NAT_JOURNAL) { - for (i = 0; i < nats_in_cursum(sum); i++) { - if (le32_to_cpu(nid_in_journal(sum, i)) == val) - return i; - } - if (alloc && nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES) - return update_nats_in_cursum(sum, 1); - } else if (type == SIT_JOURNAL) { - for (i = 0; i < sits_in_cursum(sum); i++) - if (le32_to_cpu(segno_in_journal(sum, i)) == val) - return i; - if (alloc && sits_in_cursum(sum) < SIT_JOURNAL_ENTRIES) - return update_sits_in_cursum(sum, 1); - } - return -1; -} - -static struct page *get_current_sit_page(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned int offset = SIT_BLOCK_OFFSET(sit_i, segno); - block_t blk_addr = sit_i->sit_base_addr + offset; - - check_seg_range(sbi, segno); - - /* calculate sit block address */ - if (f2fs_test_bit(offset, sit_i->sit_bitmap)) - blk_addr += sit_i->sit_blocks; - - return get_meta_page(sbi, blk_addr); -} - -static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, - unsigned int start) -{ - struct sit_info *sit_i = SIT_I(sbi); - struct page *src_page, *dst_page; - pgoff_t src_off, dst_off; - void *src_addr, *dst_addr; - - src_off = current_sit_addr(sbi, start); - dst_off = next_sit_addr(sbi, src_off); - - /* get current sit block page without lock */ - src_page = get_meta_page(sbi, src_off); - dst_page = grab_meta_page(sbi, dst_off); - BUG_ON(PageDirty(src_page)); - - src_addr = page_address(src_page); - dst_addr = page_address(dst_page); - memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE); - - set_page_dirty(dst_page); - f2fs_put_page(src_page, 1); - - set_to_next_sit(sit_i, start); - - return dst_page; -} - -static bool flush_sits_in_journal(struct f2fs_sb_info *sbi) -{ - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - int i; - - /* - * If the journal area in the current summary is full of sit entries, - * all the sit entries will be flushed. Otherwise the sit entries - * are not able to replace with newly hot sit entries. - */ - if (sits_in_cursum(sum) >= SIT_JOURNAL_ENTRIES) { - for (i = sits_in_cursum(sum) - 1; i >= 0; i--) { - unsigned int segno; - segno = le32_to_cpu(segno_in_journal(sum, i)); - __mark_sit_entry_dirty(sbi, segno); - } - update_sits_in_cursum(sum, -sits_in_cursum(sum)); - return 1; - } - return 0; -} - -/* - * CP calls this function, which flushes SIT entries including sit_journal, - * and moves prefree segs to free segs. - */ -void flush_sit_entries(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned long *bitmap = sit_i->dirty_sentries_bitmap; - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - unsigned long nsegs = TOTAL_SEGS(sbi); - struct page *page = NULL; - struct f2fs_sit_block *raw_sit = NULL; - unsigned int start = 0, end = 0; - unsigned int segno = -1; - bool flushed; - - mutex_lock(&curseg->curseg_mutex); - mutex_lock(&sit_i->sentry_lock); - - /* - * "flushed" indicates whether sit entries in journal are flushed - * to the SIT area or not. - */ - flushed = flush_sits_in_journal(sbi); - - while ((segno = find_next_bit(bitmap, nsegs, segno + 1)) < nsegs) { - struct seg_entry *se = get_seg_entry(sbi, segno); - int sit_offset, offset; - - sit_offset = SIT_ENTRY_OFFSET(sit_i, segno); - - if (flushed) - goto to_sit_page; - - offset = lookup_journal_in_cursum(sum, SIT_JOURNAL, segno, 1); - if (offset >= 0) { - segno_in_journal(sum, offset) = cpu_to_le32(segno); - seg_info_to_raw_sit(se, &sit_in_journal(sum, offset)); - goto flush_done; - } -to_sit_page: - if (!page || (start > segno) || (segno > end)) { - if (page) { - f2fs_put_page(page, 1); - page = NULL; - } - - start = START_SEGNO(sit_i, segno); - end = start + SIT_ENTRY_PER_BLOCK - 1; - - /* read sit block that will be updated */ - page = get_next_sit_page(sbi, start); - raw_sit = page_address(page); - } - - /* udpate entry in SIT block */ - seg_info_to_raw_sit(se, &raw_sit->entries[sit_offset]); -flush_done: - __clear_bit(segno, bitmap); - sit_i->dirty_sentries--; - } - mutex_unlock(&sit_i->sentry_lock); - mutex_unlock(&curseg->curseg_mutex); - - /* writeout last modified SIT block */ - f2fs_put_page(page, 1); - - set_prefree_as_free_segments(sbi); -} - -static int build_sit_info(struct f2fs_sb_info *sbi) -{ - struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - struct sit_info *sit_i; - unsigned int sit_segs, start; - char *src_bitmap, *dst_bitmap; - unsigned int bitmap_size; - - /* allocate memory for SIT information */ - sit_i = kzalloc(sizeof(struct sit_info), GFP_KERNEL); - if (!sit_i) - return -ENOMEM; - - SM_I(sbi)->sit_info = sit_i; - - sit_i->sentries = vzalloc(TOTAL_SEGS(sbi) * sizeof(struct seg_entry)); - if (!sit_i->sentries) - return -ENOMEM; - - bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); - sit_i->dirty_sentries_bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!sit_i->dirty_sentries_bitmap) - return -ENOMEM; - - for (start = 0; start < TOTAL_SEGS(sbi); start++) { - sit_i->sentries[start].cur_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); - sit_i->sentries[start].ckpt_valid_map - = kzalloc(SIT_VBLOCK_MAP_SIZE, GFP_KERNEL); - if (!sit_i->sentries[start].cur_valid_map - || !sit_i->sentries[start].ckpt_valid_map) - return -ENOMEM; - } - - if (sbi->segs_per_sec > 1) { - sit_i->sec_entries = vzalloc(sbi->total_sections * - sizeof(struct sec_entry)); - if (!sit_i->sec_entries) - return -ENOMEM; - } - - /* get information related with SIT */ - sit_segs = le32_to_cpu(raw_super->segment_count_sit) >> 1; - - /* setup SIT bitmap from ckeckpoint pack */ - bitmap_size = __bitmap_size(sbi, SIT_BITMAP); - src_bitmap = __bitmap_ptr(sbi, SIT_BITMAP); - - dst_bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!dst_bitmap) - return -ENOMEM; - memcpy(dst_bitmap, src_bitmap, bitmap_size); - - /* init SIT information */ - sit_i->s_ops = &default_salloc_ops; - - sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr); - sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; - sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count); - sit_i->sit_bitmap = dst_bitmap; - sit_i->bitmap_size = bitmap_size; - sit_i->dirty_sentries = 0; - sit_i->sents_per_block = SIT_ENTRY_PER_BLOCK; - sit_i->elapsed_time = le64_to_cpu(sbi->ckpt->elapsed_time); - sit_i->mounted_time = CURRENT_TIME_SEC.tv_sec; - mutex_init(&sit_i->sentry_lock); - return 0; -} - -static int build_free_segmap(struct f2fs_sb_info *sbi) -{ - struct f2fs_sm_info *sm_info = SM_I(sbi); - struct free_segmap_info *free_i; - unsigned int bitmap_size, sec_bitmap_size; - - /* allocate memory for free segmap information */ - free_i = kzalloc(sizeof(struct free_segmap_info), GFP_KERNEL); - if (!free_i) - return -ENOMEM; - - SM_I(sbi)->free_info = free_i; - - bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); - free_i->free_segmap = kmalloc(bitmap_size, GFP_KERNEL); - if (!free_i->free_segmap) - return -ENOMEM; - - sec_bitmap_size = f2fs_bitmap_size(sbi->total_sections); - free_i->free_secmap = kmalloc(sec_bitmap_size, GFP_KERNEL); - if (!free_i->free_secmap) - return -ENOMEM; - - /* set all segments as dirty temporarily */ - memset(free_i->free_segmap, 0xff, bitmap_size); - memset(free_i->free_secmap, 0xff, sec_bitmap_size); - - /* init free segmap information */ - free_i->start_segno = - (unsigned int) GET_SEGNO_FROM_SEG0(sbi, sm_info->main_blkaddr); - free_i->free_segments = 0; - free_i->free_sections = 0; - rwlock_init(&free_i->segmap_lock); - return 0; -} - -static int build_curseg(struct f2fs_sb_info *sbi) -{ - struct curseg_info *array; - int i; - - array = kzalloc(sizeof(*array) * NR_CURSEG_TYPE, GFP_KERNEL); - if (!array) - return -ENOMEM; - - SM_I(sbi)->curseg_array = array; - - for (i = 0; i < NR_CURSEG_TYPE; i++) { - mutex_init(&array[i].curseg_mutex); - array[i].sum_blk = kzalloc(PAGE_CACHE_SIZE, GFP_KERNEL); - if (!array[i].sum_blk) - return -ENOMEM; - array[i].segno = NULL_SEGNO; - array[i].next_blkoff = 0; - } - return restore_curseg_summaries(sbi); -} - -static void build_sit_entries(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_COLD_DATA); - struct f2fs_summary_block *sum = curseg->sum_blk; - unsigned int start; - - for (start = 0; start < TOTAL_SEGS(sbi); start++) { - struct seg_entry *se = &sit_i->sentries[start]; - struct f2fs_sit_block *sit_blk; - struct f2fs_sit_entry sit; - struct page *page; - int i; - - mutex_lock(&curseg->curseg_mutex); - for (i = 0; i < sits_in_cursum(sum); i++) { - if (le32_to_cpu(segno_in_journal(sum, i)) == start) { - sit = sit_in_journal(sum, i); - mutex_unlock(&curseg->curseg_mutex); - goto got_it; - } - } - mutex_unlock(&curseg->curseg_mutex); - page = get_current_sit_page(sbi, start); - sit_blk = (struct f2fs_sit_block *)page_address(page); - sit = sit_blk->entries[SIT_ENTRY_OFFSET(sit_i, start)]; - f2fs_put_page(page, 1); -got_it: - check_block_count(sbi, start, &sit); - seg_info_from_raw_sit(se, &sit); - if (sbi->segs_per_sec > 1) { - struct sec_entry *e = get_sec_entry(sbi, start); - e->valid_blocks += se->valid_blocks; - } - } -} - -static void init_free_segmap(struct f2fs_sb_info *sbi) -{ - unsigned int start; - int type; - - for (start = 0; start < TOTAL_SEGS(sbi); start++) { - struct seg_entry *sentry = get_seg_entry(sbi, start); - if (!sentry->valid_blocks) - __set_free(sbi, start); - } - - /* set use the current segments */ - for (type = CURSEG_HOT_DATA; type <= CURSEG_COLD_NODE; type++) { - struct curseg_info *curseg_t = CURSEG_I(sbi, type); - __set_test_and_inuse(sbi, curseg_t->segno); - } -} - -static void init_dirty_segmap(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int segno = 0, offset = 0; - unsigned short valid_blocks; - - while (segno < TOTAL_SEGS(sbi)) { - /* find dirty segment based on free segmap */ - segno = find_next_inuse(free_i, TOTAL_SEGS(sbi), offset); - if (segno >= TOTAL_SEGS(sbi)) - break; - offset = segno + 1; - valid_blocks = get_valid_blocks(sbi, segno, 0); - if (valid_blocks >= sbi->blocks_per_seg || !valid_blocks) - continue; - mutex_lock(&dirty_i->seglist_lock); - __locate_dirty_segment(sbi, segno, DIRTY); - mutex_unlock(&dirty_i->seglist_lock); - } -} - -static int init_victim_segmap(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); - - dirty_i->victim_segmap[FG_GC] = kzalloc(bitmap_size, GFP_KERNEL); - dirty_i->victim_segmap[BG_GC] = kzalloc(bitmap_size, GFP_KERNEL); - if (!dirty_i->victim_segmap[FG_GC] || !dirty_i->victim_segmap[BG_GC]) - return -ENOMEM; - return 0; -} - -static int build_dirty_segmap(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i; - unsigned int bitmap_size, i; - - /* allocate memory for dirty segments list information */ - dirty_i = kzalloc(sizeof(struct dirty_seglist_info), GFP_KERNEL); - if (!dirty_i) - return -ENOMEM; - - SM_I(sbi)->dirty_info = dirty_i; - mutex_init(&dirty_i->seglist_lock); - - bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); - - for (i = 0; i < NR_DIRTY_TYPE; i++) { - dirty_i->dirty_segmap[i] = kzalloc(bitmap_size, GFP_KERNEL); - dirty_i->nr_dirty[i] = 0; - if (!dirty_i->dirty_segmap[i]) - return -ENOMEM; - } - - init_dirty_segmap(sbi); - return init_victim_segmap(sbi); -} - -/* - * Update min, max modified time for cost-benefit GC algorithm - */ -static void init_min_max_mtime(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned int segno; - - mutex_lock(&sit_i->sentry_lock); - - sit_i->min_mtime = LLONG_MAX; - - for (segno = 0; segno < TOTAL_SEGS(sbi); segno += sbi->segs_per_sec) { - unsigned int i; - unsigned long long mtime = 0; - - for (i = 0; i < sbi->segs_per_sec; i++) - mtime += get_seg_entry(sbi, segno + i)->mtime; - - mtime = div_u64(mtime, sbi->segs_per_sec); - - if (sit_i->min_mtime > mtime) - sit_i->min_mtime = mtime; - } - sit_i->max_mtime = get_mtime(sbi); - mutex_unlock(&sit_i->sentry_lock); -} - -int build_segment_manager(struct f2fs_sb_info *sbi) -{ - struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); - struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi); - struct f2fs_sm_info *sm_info; - int err; - - sm_info = kzalloc(sizeof(struct f2fs_sm_info), GFP_KERNEL); - if (!sm_info) - return -ENOMEM; - - /* init sm info */ - sbi->sm_info = sm_info; - INIT_LIST_HEAD(&sm_info->wblist_head); - spin_lock_init(&sm_info->wblist_lock); - sm_info->seg0_blkaddr = le32_to_cpu(raw_super->segment0_blkaddr); - sm_info->main_blkaddr = le32_to_cpu(raw_super->main_blkaddr); - sm_info->segment_count = le32_to_cpu(raw_super->segment_count); - sm_info->reserved_segments = le32_to_cpu(ckpt->rsvd_segment_count); - sm_info->ovp_segments = le32_to_cpu(ckpt->overprov_segment_count); - sm_info->main_segments = le32_to_cpu(raw_super->segment_count_main); - sm_info->ssa_blkaddr = le32_to_cpu(raw_super->ssa_blkaddr); - - err = build_sit_info(sbi); - if (err) - return err; - err = build_free_segmap(sbi); - if (err) - return err; - err = build_curseg(sbi); - if (err) - return err; - - /* reinit free segmap based on SIT */ - build_sit_entries(sbi); - - init_free_segmap(sbi); - err = build_dirty_segmap(sbi); - if (err) - return err; - - init_min_max_mtime(sbi); - return 0; -} - -static void discard_dirty_segmap(struct f2fs_sb_info *sbi, - enum dirty_type dirty_type) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - - mutex_lock(&dirty_i->seglist_lock); - kfree(dirty_i->dirty_segmap[dirty_type]); - dirty_i->nr_dirty[dirty_type] = 0; - mutex_unlock(&dirty_i->seglist_lock); -} - -void reset_victim_segmap(struct f2fs_sb_info *sbi) -{ - unsigned int bitmap_size = f2fs_bitmap_size(TOTAL_SEGS(sbi)); - memset(DIRTY_I(sbi)->victim_segmap[FG_GC], 0, bitmap_size); -} - -static void destroy_victim_segmap(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - - kfree(dirty_i->victim_segmap[FG_GC]); - kfree(dirty_i->victim_segmap[BG_GC]); -} - -static void destroy_dirty_segmap(struct f2fs_sb_info *sbi) -{ - struct dirty_seglist_info *dirty_i = DIRTY_I(sbi); - int i; - - if (!dirty_i) - return; - - /* discard pre-free/dirty segments list */ - for (i = 0; i < NR_DIRTY_TYPE; i++) - discard_dirty_segmap(sbi, i); - - destroy_victim_segmap(sbi); - SM_I(sbi)->dirty_info = NULL; - kfree(dirty_i); -} - -static void destroy_curseg(struct f2fs_sb_info *sbi) -{ - struct curseg_info *array = SM_I(sbi)->curseg_array; - int i; - - if (!array) - return; - SM_I(sbi)->curseg_array = NULL; - for (i = 0; i < NR_CURSEG_TYPE; i++) - kfree(array[i].sum_blk); - kfree(array); -} - -static void destroy_free_segmap(struct f2fs_sb_info *sbi) -{ - struct free_segmap_info *free_i = SM_I(sbi)->free_info; - if (!free_i) - return; - SM_I(sbi)->free_info = NULL; - kfree(free_i->free_segmap); - kfree(free_i->free_secmap); - kfree(free_i); -} - -static void destroy_sit_info(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned int start; - - if (!sit_i) - return; - - if (sit_i->sentries) { - for (start = 0; start < TOTAL_SEGS(sbi); start++) { - kfree(sit_i->sentries[start].cur_valid_map); - kfree(sit_i->sentries[start].ckpt_valid_map); - } - } - vfree(sit_i->sentries); - vfree(sit_i->sec_entries); - kfree(sit_i->dirty_sentries_bitmap); - - SM_I(sbi)->sit_info = NULL; - kfree(sit_i->sit_bitmap); - kfree(sit_i); -} - -void destroy_segment_manager(struct f2fs_sb_info *sbi) -{ - struct f2fs_sm_info *sm_info = SM_I(sbi); - destroy_dirty_segmap(sbi); - destroy_curseg(sbi); - destroy_free_segmap(sbi); - destroy_sit_info(sbi); - sbi->sm_info = NULL; - kfree(sm_info); -} diff --git a/trunk/fs/f2fs/segment.h b/trunk/fs/f2fs/segment.h deleted file mode 100644 index 0948405af6f5..000000000000 --- a/trunk/fs/f2fs/segment.h +++ /dev/null @@ -1,618 +0,0 @@ -/* - * fs/f2fs/segment.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -/* constant macro */ -#define NULL_SEGNO ((unsigned int)(~0)) - -/* V: Logical segment # in volume, R: Relative segment # in main area */ -#define GET_L2R_SEGNO(free_i, segno) (segno - free_i->start_segno) -#define GET_R2L_SEGNO(free_i, segno) (segno + free_i->start_segno) - -#define IS_DATASEG(t) \ - ((t == CURSEG_HOT_DATA) || (t == CURSEG_COLD_DATA) || \ - (t == CURSEG_WARM_DATA)) - -#define IS_NODESEG(t) \ - ((t == CURSEG_HOT_NODE) || (t == CURSEG_COLD_NODE) || \ - (t == CURSEG_WARM_NODE)) - -#define IS_CURSEG(sbi, segno) \ - ((segno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno) || \ - (segno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno) || \ - (segno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno) || \ - (segno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno) || \ - (segno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno) || \ - (segno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno)) - -#define IS_CURSEC(sbi, secno) \ - ((secno == CURSEG_I(sbi, CURSEG_HOT_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_WARM_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_COLD_DATA)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_HOT_NODE)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_WARM_NODE)->segno / \ - sbi->segs_per_sec) || \ - (secno == CURSEG_I(sbi, CURSEG_COLD_NODE)->segno / \ - sbi->segs_per_sec)) \ - -#define START_BLOCK(sbi, segno) \ - (SM_I(sbi)->seg0_blkaddr + \ - (GET_R2L_SEGNO(FREE_I(sbi), segno) << sbi->log_blocks_per_seg)) -#define NEXT_FREE_BLKADDR(sbi, curseg) \ - (START_BLOCK(sbi, curseg->segno) + curseg->next_blkoff) - -#define MAIN_BASE_BLOCK(sbi) (SM_I(sbi)->main_blkaddr) - -#define GET_SEGOFF_FROM_SEG0(sbi, blk_addr) \ - ((blk_addr) - SM_I(sbi)->seg0_blkaddr) -#define GET_SEGNO_FROM_SEG0(sbi, blk_addr) \ - (GET_SEGOFF_FROM_SEG0(sbi, blk_addr) >> sbi->log_blocks_per_seg) -#define GET_SEGNO(sbi, blk_addr) \ - (((blk_addr == NULL_ADDR) || (blk_addr == NEW_ADDR)) ? \ - NULL_SEGNO : GET_L2R_SEGNO(FREE_I(sbi), \ - GET_SEGNO_FROM_SEG0(sbi, blk_addr))) -#define GET_SECNO(sbi, segno) \ - ((segno) / sbi->segs_per_sec) -#define GET_ZONENO_FROM_SEGNO(sbi, segno) \ - ((segno / sbi->segs_per_sec) / sbi->secs_per_zone) - -#define GET_SUM_BLOCK(sbi, segno) \ - ((sbi->sm_info->ssa_blkaddr) + segno) - -#define GET_SUM_TYPE(footer) ((footer)->entry_type) -#define SET_SUM_TYPE(footer, type) ((footer)->entry_type = type) - -#define SIT_ENTRY_OFFSET(sit_i, segno) \ - (segno % sit_i->sents_per_block) -#define SIT_BLOCK_OFFSET(sit_i, segno) \ - (segno / SIT_ENTRY_PER_BLOCK) -#define START_SEGNO(sit_i, segno) \ - (SIT_BLOCK_OFFSET(sit_i, segno) * SIT_ENTRY_PER_BLOCK) -#define f2fs_bitmap_size(nr) \ - (BITS_TO_LONGS(nr) * sizeof(unsigned long)) -#define TOTAL_SEGS(sbi) (SM_I(sbi)->main_segments) - -#define SECTOR_FROM_BLOCK(sbi, blk_addr) \ - (blk_addr << ((sbi)->log_blocksize - F2FS_LOG_SECTOR_SIZE)) - -/* during checkpoint, bio_private is used to synchronize the last bio */ -struct bio_private { - struct f2fs_sb_info *sbi; - bool is_sync; - void *wait; -}; - -/* - * indicate a block allocation direction: RIGHT and LEFT. - * RIGHT means allocating new sections towards the end of volume. - * LEFT means the opposite direction. - */ -enum { - ALLOC_RIGHT = 0, - ALLOC_LEFT -}; - -/* - * In the victim_sel_policy->alloc_mode, there are two block allocation modes. - * LFS writes data sequentially with cleaning operations. - * SSR (Slack Space Recycle) reuses obsolete space without cleaning operations. - */ -enum { - LFS = 0, - SSR -}; - -/* - * In the victim_sel_policy->gc_mode, there are two gc, aka cleaning, modes. - * GC_CB is based on cost-benefit algorithm. - * GC_GREEDY is based on greedy algorithm. - */ -enum { - GC_CB = 0, - GC_GREEDY -}; - -/* - * BG_GC means the background cleaning job. - * FG_GC means the on-demand cleaning job. - */ -enum { - BG_GC = 0, - FG_GC -}; - -/* for a function parameter to select a victim segment */ -struct victim_sel_policy { - int alloc_mode; /* LFS or SSR */ - int gc_mode; /* GC_CB or GC_GREEDY */ - unsigned long *dirty_segmap; /* dirty segment bitmap */ - unsigned int offset; /* last scanned bitmap offset */ - unsigned int ofs_unit; /* bitmap search unit */ - unsigned int min_cost; /* minimum cost */ - unsigned int min_segno; /* segment # having min. cost */ -}; - -struct seg_entry { - unsigned short valid_blocks; /* # of valid blocks */ - unsigned char *cur_valid_map; /* validity bitmap of blocks */ - /* - * # of valid blocks and the validity bitmap stored in the the last - * checkpoint pack. This information is used by the SSR mode. - */ - unsigned short ckpt_valid_blocks; - unsigned char *ckpt_valid_map; - unsigned char type; /* segment type like CURSEG_XXX_TYPE */ - unsigned long long mtime; /* modification time of the segment */ -}; - -struct sec_entry { - unsigned int valid_blocks; /* # of valid blocks in a section */ -}; - -struct segment_allocation { - void (*allocate_segment)(struct f2fs_sb_info *, int, bool); -}; - -struct sit_info { - const struct segment_allocation *s_ops; - - block_t sit_base_addr; /* start block address of SIT area */ - block_t sit_blocks; /* # of blocks used by SIT area */ - block_t written_valid_blocks; /* # of valid blocks in main area */ - char *sit_bitmap; /* SIT bitmap pointer */ - unsigned int bitmap_size; /* SIT bitmap size */ - - unsigned long *dirty_sentries_bitmap; /* bitmap for dirty sentries */ - unsigned int dirty_sentries; /* # of dirty sentries */ - unsigned int sents_per_block; /* # of SIT entries per block */ - struct mutex sentry_lock; /* to protect SIT cache */ - struct seg_entry *sentries; /* SIT segment-level cache */ - struct sec_entry *sec_entries; /* SIT section-level cache */ - - /* for cost-benefit algorithm in cleaning procedure */ - unsigned long long elapsed_time; /* elapsed time after mount */ - unsigned long long mounted_time; /* mount time */ - unsigned long long min_mtime; /* min. modification time */ - unsigned long long max_mtime; /* max. modification time */ -}; - -struct free_segmap_info { - unsigned int start_segno; /* start segment number logically */ - unsigned int free_segments; /* # of free segments */ - unsigned int free_sections; /* # of free sections */ - rwlock_t segmap_lock; /* free segmap lock */ - unsigned long *free_segmap; /* free segment bitmap */ - unsigned long *free_secmap; /* free section bitmap */ -}; - -/* Notice: The order of dirty type is same with CURSEG_XXX in f2fs.h */ -enum dirty_type { - DIRTY_HOT_DATA, /* dirty segments assigned as hot data logs */ - DIRTY_WARM_DATA, /* dirty segments assigned as warm data logs */ - DIRTY_COLD_DATA, /* dirty segments assigned as cold data logs */ - DIRTY_HOT_NODE, /* dirty segments assigned as hot node logs */ - DIRTY_WARM_NODE, /* dirty segments assigned as warm node logs */ - DIRTY_COLD_NODE, /* dirty segments assigned as cold node logs */ - DIRTY, /* to count # of dirty segments */ - PRE, /* to count # of entirely obsolete segments */ - NR_DIRTY_TYPE -}; - -struct dirty_seglist_info { - const struct victim_selection *v_ops; /* victim selction operation */ - unsigned long *dirty_segmap[NR_DIRTY_TYPE]; - struct mutex seglist_lock; /* lock for segment bitmaps */ - int nr_dirty[NR_DIRTY_TYPE]; /* # of dirty segments */ - unsigned long *victim_segmap[2]; /* BG_GC, FG_GC */ -}; - -/* victim selection function for cleaning and SSR */ -struct victim_selection { - int (*get_victim)(struct f2fs_sb_info *, unsigned int *, - int, int, char); -}; - -/* for active log information */ -struct curseg_info { - struct mutex curseg_mutex; /* lock for consistency */ - struct f2fs_summary_block *sum_blk; /* cached summary block */ - unsigned char alloc_type; /* current allocation type */ - unsigned int segno; /* current segment number */ - unsigned short next_blkoff; /* next block offset to write */ - unsigned int zone; /* current zone number */ - unsigned int next_segno; /* preallocated segment */ -}; - -/* - * inline functions - */ -static inline struct curseg_info *CURSEG_I(struct f2fs_sb_info *sbi, int type) -{ - return (struct curseg_info *)(SM_I(sbi)->curseg_array + type); -} - -static inline struct seg_entry *get_seg_entry(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct sit_info *sit_i = SIT_I(sbi); - return &sit_i->sentries[segno]; -} - -static inline struct sec_entry *get_sec_entry(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct sit_info *sit_i = SIT_I(sbi); - return &sit_i->sec_entries[GET_SECNO(sbi, segno)]; -} - -static inline unsigned int get_valid_blocks(struct f2fs_sb_info *sbi, - unsigned int segno, int section) -{ - /* - * In order to get # of valid blocks in a section instantly from many - * segments, f2fs manages two counting structures separately. - */ - if (section > 1) - return get_sec_entry(sbi, segno)->valid_blocks; - else - return get_seg_entry(sbi, segno)->valid_blocks; -} - -static inline void seg_info_from_raw_sit(struct seg_entry *se, - struct f2fs_sit_entry *rs) -{ - se->valid_blocks = GET_SIT_VBLOCKS(rs); - se->ckpt_valid_blocks = GET_SIT_VBLOCKS(rs); - memcpy(se->cur_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); - memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); - se->type = GET_SIT_TYPE(rs); - se->mtime = le64_to_cpu(rs->mtime); -} - -static inline void seg_info_to_raw_sit(struct seg_entry *se, - struct f2fs_sit_entry *rs) -{ - unsigned short raw_vblocks = (se->type << SIT_VBLOCKS_SHIFT) | - se->valid_blocks; - rs->vblocks = cpu_to_le16(raw_vblocks); - memcpy(rs->valid_map, se->cur_valid_map, SIT_VBLOCK_MAP_SIZE); - memcpy(se->ckpt_valid_map, rs->valid_map, SIT_VBLOCK_MAP_SIZE); - se->ckpt_valid_blocks = se->valid_blocks; - rs->mtime = cpu_to_le64(se->mtime); -} - -static inline unsigned int find_next_inuse(struct free_segmap_info *free_i, - unsigned int max, unsigned int segno) -{ - unsigned int ret; - read_lock(&free_i->segmap_lock); - ret = find_next_bit(free_i->free_segmap, max, segno); - read_unlock(&free_i->segmap_lock); - return ret; -} - -static inline void __set_free(struct f2fs_sb_info *sbi, unsigned int segno) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - unsigned int start_segno = secno * sbi->segs_per_sec; - unsigned int next; - - write_lock(&free_i->segmap_lock); - clear_bit(segno, free_i->free_segmap); - free_i->free_segments++; - - next = find_next_bit(free_i->free_segmap, TOTAL_SEGS(sbi), start_segno); - if (next >= start_segno + sbi->segs_per_sec) { - clear_bit(secno, free_i->free_secmap); - free_i->free_sections++; - } - write_unlock(&free_i->segmap_lock); -} - -static inline void __set_inuse(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - set_bit(segno, free_i->free_segmap); - free_i->free_segments--; - if (!test_and_set_bit(secno, free_i->free_secmap)) - free_i->free_sections--; -} - -static inline void __set_test_and_free(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - unsigned int start_segno = secno * sbi->segs_per_sec; - unsigned int next; - - write_lock(&free_i->segmap_lock); - if (test_and_clear_bit(segno, free_i->free_segmap)) { - free_i->free_segments++; - - next = find_next_bit(free_i->free_segmap, TOTAL_SEGS(sbi), - start_segno); - if (next >= start_segno + sbi->segs_per_sec) { - if (test_and_clear_bit(secno, free_i->free_secmap)) - free_i->free_sections++; - } - } - write_unlock(&free_i->segmap_lock); -} - -static inline void __set_test_and_inuse(struct f2fs_sb_info *sbi, - unsigned int segno) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int secno = segno / sbi->segs_per_sec; - write_lock(&free_i->segmap_lock); - if (!test_and_set_bit(segno, free_i->free_segmap)) { - free_i->free_segments--; - if (!test_and_set_bit(secno, free_i->free_secmap)) - free_i->free_sections--; - } - write_unlock(&free_i->segmap_lock); -} - -static inline void get_sit_bitmap(struct f2fs_sb_info *sbi, - void *dst_addr) -{ - struct sit_info *sit_i = SIT_I(sbi); - memcpy(dst_addr, sit_i->sit_bitmap, sit_i->bitmap_size); -} - -static inline block_t written_block_count(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - block_t vblocks; - - mutex_lock(&sit_i->sentry_lock); - vblocks = sit_i->written_valid_blocks; - mutex_unlock(&sit_i->sentry_lock); - - return vblocks; -} - -static inline unsigned int free_segments(struct f2fs_sb_info *sbi) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int free_segs; - - read_lock(&free_i->segmap_lock); - free_segs = free_i->free_segments; - read_unlock(&free_i->segmap_lock); - - return free_segs; -} - -static inline int reserved_segments(struct f2fs_sb_info *sbi) -{ - return SM_I(sbi)->reserved_segments; -} - -static inline unsigned int free_sections(struct f2fs_sb_info *sbi) -{ - struct free_segmap_info *free_i = FREE_I(sbi); - unsigned int free_secs; - - read_lock(&free_i->segmap_lock); - free_secs = free_i->free_sections; - read_unlock(&free_i->segmap_lock); - - return free_secs; -} - -static inline unsigned int prefree_segments(struct f2fs_sb_info *sbi) -{ - return DIRTY_I(sbi)->nr_dirty[PRE]; -} - -static inline unsigned int dirty_segments(struct f2fs_sb_info *sbi) -{ - return DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_DATA] + - DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_DATA] + - DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_DATA] + - DIRTY_I(sbi)->nr_dirty[DIRTY_HOT_NODE] + - DIRTY_I(sbi)->nr_dirty[DIRTY_WARM_NODE] + - DIRTY_I(sbi)->nr_dirty[DIRTY_COLD_NODE]; -} - -static inline int overprovision_segments(struct f2fs_sb_info *sbi) -{ - return SM_I(sbi)->ovp_segments; -} - -static inline int overprovision_sections(struct f2fs_sb_info *sbi) -{ - return ((unsigned int) overprovision_segments(sbi)) / sbi->segs_per_sec; -} - -static inline int reserved_sections(struct f2fs_sb_info *sbi) -{ - return ((unsigned int) reserved_segments(sbi)) / sbi->segs_per_sec; -} - -static inline bool need_SSR(struct f2fs_sb_info *sbi) -{ - return (free_sections(sbi) < overprovision_sections(sbi)); -} - -static inline int get_ssr_segment(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return DIRTY_I(sbi)->v_ops->get_victim(sbi, - &(curseg)->next_segno, BG_GC, type, SSR); -} - -static inline bool has_not_enough_free_secs(struct f2fs_sb_info *sbi) -{ - return free_sections(sbi) <= reserved_sections(sbi); -} - -static inline int utilization(struct f2fs_sb_info *sbi) -{ - return (long int)valid_user_blocks(sbi) * 100 / - (long int)sbi->user_block_count; -} - -/* - * Sometimes f2fs may be better to drop out-of-place update policy. - * So, if fs utilization is over MIN_IPU_UTIL, then f2fs tries to write - * data in the original place likewise other traditional file systems. - * But, currently set 100 in percentage, which means it is disabled. - * See below need_inplace_update(). - */ -#define MIN_IPU_UTIL 100 -static inline bool need_inplace_update(struct inode *inode) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - if (S_ISDIR(inode->i_mode)) - return false; - if (need_SSR(sbi) && utilization(sbi) > MIN_IPU_UTIL) - return true; - return false; -} - -static inline unsigned int curseg_segno(struct f2fs_sb_info *sbi, - int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return curseg->segno; -} - -static inline unsigned char curseg_alloc_type(struct f2fs_sb_info *sbi, - int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return curseg->alloc_type; -} - -static inline unsigned short curseg_blkoff(struct f2fs_sb_info *sbi, int type) -{ - struct curseg_info *curseg = CURSEG_I(sbi, type); - return curseg->next_blkoff; -} - -static inline void check_seg_range(struct f2fs_sb_info *sbi, unsigned int segno) -{ - unsigned int end_segno = SM_I(sbi)->segment_count - 1; - BUG_ON(segno > end_segno); -} - -/* - * This function is used for only debugging. - * NOTE: In future, we have to remove this function. - */ -static inline void verify_block_addr(struct f2fs_sb_info *sbi, block_t blk_addr) -{ - struct f2fs_sm_info *sm_info = SM_I(sbi); - block_t total_blks = sm_info->segment_count << sbi->log_blocks_per_seg; - block_t start_addr = sm_info->seg0_blkaddr; - block_t end_addr = start_addr + total_blks - 1; - BUG_ON(blk_addr < start_addr); - BUG_ON(blk_addr > end_addr); -} - -/* - * Summary block is always treated as invalid block - */ -static inline void check_block_count(struct f2fs_sb_info *sbi, - int segno, struct f2fs_sit_entry *raw_sit) -{ - struct f2fs_sm_info *sm_info = SM_I(sbi); - unsigned int end_segno = sm_info->segment_count - 1; - int valid_blocks = 0; - int i; - - /* check segment usage */ - BUG_ON(GET_SIT_VBLOCKS(raw_sit) > sbi->blocks_per_seg); - - /* check boundary of a given segment number */ - BUG_ON(segno > end_segno); - - /* check bitmap with valid block count */ - for (i = 0; i < sbi->blocks_per_seg; i++) - if (f2fs_test_bit(i, raw_sit->valid_map)) - valid_blocks++; - BUG_ON(GET_SIT_VBLOCKS(raw_sit) != valid_blocks); -} - -static inline pgoff_t current_sit_addr(struct f2fs_sb_info *sbi, - unsigned int start) -{ - struct sit_info *sit_i = SIT_I(sbi); - unsigned int offset = SIT_BLOCK_OFFSET(sit_i, start); - block_t blk_addr = sit_i->sit_base_addr + offset; - - check_seg_range(sbi, start); - - /* calculate sit block address */ - if (f2fs_test_bit(offset, sit_i->sit_bitmap)) - blk_addr += sit_i->sit_blocks; - - return blk_addr; -} - -static inline pgoff_t next_sit_addr(struct f2fs_sb_info *sbi, - pgoff_t block_addr) -{ - struct sit_info *sit_i = SIT_I(sbi); - block_addr -= sit_i->sit_base_addr; - if (block_addr < sit_i->sit_blocks) - block_addr += sit_i->sit_blocks; - else - block_addr -= sit_i->sit_blocks; - - return block_addr + sit_i->sit_base_addr; -} - -static inline void set_to_next_sit(struct sit_info *sit_i, unsigned int start) -{ - unsigned int block_off = SIT_BLOCK_OFFSET(sit_i, start); - - if (f2fs_test_bit(block_off, sit_i->sit_bitmap)) - f2fs_clear_bit(block_off, sit_i->sit_bitmap); - else - f2fs_set_bit(block_off, sit_i->sit_bitmap); -} - -static inline unsigned long long get_mtime(struct f2fs_sb_info *sbi) -{ - struct sit_info *sit_i = SIT_I(sbi); - return sit_i->elapsed_time + CURRENT_TIME_SEC.tv_sec - - sit_i->mounted_time; -} - -static inline void set_summary(struct f2fs_summary *sum, nid_t nid, - unsigned int ofs_in_node, unsigned char version) -{ - sum->nid = cpu_to_le32(nid); - sum->ofs_in_node = cpu_to_le16(ofs_in_node); - sum->version = version; -} - -static inline block_t start_sum_block(struct f2fs_sb_info *sbi) -{ - return __start_cp_addr(sbi) + - le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); -} - -static inline block_t sum_blk_addr(struct f2fs_sb_info *sbi, int base, int type) -{ - return __start_cp_addr(sbi) + - le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_total_block_count) - - (base + 1) + type; -} diff --git a/trunk/fs/f2fs/super.c b/trunk/fs/f2fs/super.c deleted file mode 100644 index 13867322cf5a..000000000000 --- a/trunk/fs/f2fs/super.c +++ /dev/null @@ -1,657 +0,0 @@ -/* - * fs/f2fs/super.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "f2fs.h" -#include "node.h" -#include "xattr.h" - -static struct kmem_cache *f2fs_inode_cachep; - -enum { - Opt_gc_background_off, - Opt_disable_roll_forward, - Opt_discard, - Opt_noheap, - Opt_nouser_xattr, - Opt_noacl, - Opt_active_logs, - Opt_disable_ext_identify, - Opt_err, -}; - -static match_table_t f2fs_tokens = { - {Opt_gc_background_off, "background_gc_off"}, - {Opt_disable_roll_forward, "disable_roll_forward"}, - {Opt_discard, "discard"}, - {Opt_noheap, "no_heap"}, - {Opt_nouser_xattr, "nouser_xattr"}, - {Opt_noacl, "noacl"}, - {Opt_active_logs, "active_logs=%u"}, - {Opt_disable_ext_identify, "disable_ext_identify"}, - {Opt_err, NULL}, -}; - -static void init_once(void *foo) -{ - struct f2fs_inode_info *fi = (struct f2fs_inode_info *) foo; - - inode_init_once(&fi->vfs_inode); -} - -static struct inode *f2fs_alloc_inode(struct super_block *sb) -{ - struct f2fs_inode_info *fi; - - fi = kmem_cache_alloc(f2fs_inode_cachep, GFP_NOFS | __GFP_ZERO); - if (!fi) - return NULL; - - init_once((void *) fi); - - /* Initilize f2fs-specific inode info */ - fi->vfs_inode.i_version = 1; - atomic_set(&fi->dirty_dents, 0); - fi->i_current_depth = 1; - fi->i_advise = 0; - rwlock_init(&fi->ext.ext_lock); - - set_inode_flag(fi, FI_NEW_INODE); - - return &fi->vfs_inode; -} - -static void f2fs_i_callback(struct rcu_head *head) -{ - struct inode *inode = container_of(head, struct inode, i_rcu); - kmem_cache_free(f2fs_inode_cachep, F2FS_I(inode)); -} - -static void f2fs_destroy_inode(struct inode *inode) -{ - call_rcu(&inode->i_rcu, f2fs_i_callback); -} - -static void f2fs_put_super(struct super_block *sb) -{ - struct f2fs_sb_info *sbi = F2FS_SB(sb); - - f2fs_destroy_stats(sbi); - stop_gc_thread(sbi); - - write_checkpoint(sbi, false, true); - - iput(sbi->node_inode); - iput(sbi->meta_inode); - - /* destroy f2fs internal modules */ - destroy_node_manager(sbi); - destroy_segment_manager(sbi); - - kfree(sbi->ckpt); - - sb->s_fs_info = NULL; - brelse(sbi->raw_super_buf); - kfree(sbi); -} - -int f2fs_sync_fs(struct super_block *sb, int sync) -{ - struct f2fs_sb_info *sbi = F2FS_SB(sb); - int ret = 0; - - if (!sbi->s_dirty && !get_pages(sbi, F2FS_DIRTY_NODES)) - return 0; - - if (sync) - write_checkpoint(sbi, false, false); - - return ret; -} - -static int f2fs_statfs(struct dentry *dentry, struct kstatfs *buf) -{ - struct super_block *sb = dentry->d_sb; - struct f2fs_sb_info *sbi = F2FS_SB(sb); - u64 id = huge_encode_dev(sb->s_bdev->bd_dev); - block_t total_count, user_block_count, start_count, ovp_count; - - total_count = le64_to_cpu(sbi->raw_super->block_count); - user_block_count = sbi->user_block_count; - start_count = le32_to_cpu(sbi->raw_super->segment0_blkaddr); - ovp_count = SM_I(sbi)->ovp_segments << sbi->log_blocks_per_seg; - buf->f_type = F2FS_SUPER_MAGIC; - buf->f_bsize = sbi->blocksize; - - buf->f_blocks = total_count - start_count; - buf->f_bfree = buf->f_blocks - valid_user_blocks(sbi) - ovp_count; - buf->f_bavail = user_block_count - valid_user_blocks(sbi); - - buf->f_files = valid_inode_count(sbi); - buf->f_ffree = sbi->total_node_count - valid_node_count(sbi); - - buf->f_namelen = F2FS_MAX_NAME_LEN; - buf->f_fsid.val[0] = (u32)id; - buf->f_fsid.val[1] = (u32)(id >> 32); - - return 0; -} - -static int f2fs_show_options(struct seq_file *seq, struct dentry *root) -{ - struct f2fs_sb_info *sbi = F2FS_SB(root->d_sb); - - if (test_opt(sbi, BG_GC)) - seq_puts(seq, ",background_gc_on"); - else - seq_puts(seq, ",background_gc_off"); - if (test_opt(sbi, DISABLE_ROLL_FORWARD)) - seq_puts(seq, ",disable_roll_forward"); - if (test_opt(sbi, DISCARD)) - seq_puts(seq, ",discard"); - if (test_opt(sbi, NOHEAP)) - seq_puts(seq, ",no_heap_alloc"); -#ifdef CONFIG_F2FS_FS_XATTR - if (test_opt(sbi, XATTR_USER)) - seq_puts(seq, ",user_xattr"); - else - seq_puts(seq, ",nouser_xattr"); -#endif -#ifdef CONFIG_F2FS_FS_POSIX_ACL - if (test_opt(sbi, POSIX_ACL)) - seq_puts(seq, ",acl"); - else - seq_puts(seq, ",noacl"); -#endif - if (test_opt(sbi, DISABLE_EXT_IDENTIFY)) - seq_puts(seq, ",disable_ext_indentify"); - - seq_printf(seq, ",active_logs=%u", sbi->active_logs); - - return 0; -} - -static struct super_operations f2fs_sops = { - .alloc_inode = f2fs_alloc_inode, - .destroy_inode = f2fs_destroy_inode, - .write_inode = f2fs_write_inode, - .show_options = f2fs_show_options, - .evict_inode = f2fs_evict_inode, - .put_super = f2fs_put_super, - .sync_fs = f2fs_sync_fs, - .statfs = f2fs_statfs, -}; - -static struct inode *f2fs_nfs_get_inode(struct super_block *sb, - u64 ino, u32 generation) -{ - struct f2fs_sb_info *sbi = F2FS_SB(sb); - struct inode *inode; - - if (ino < F2FS_ROOT_INO(sbi)) - return ERR_PTR(-ESTALE); - - /* - * f2fs_iget isn't quite right if the inode is currently unallocated! - * However f2fs_iget currently does appropriate checks to handle stale - * inodes so everything is OK. - */ - inode = f2fs_iget(sb, ino); - if (IS_ERR(inode)) - return ERR_CAST(inode); - if (generation && inode->i_generation != generation) { - /* we didn't find the right inode.. */ - iput(inode); - return ERR_PTR(-ESTALE); - } - return inode; -} - -static struct dentry *f2fs_fh_to_dentry(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - return generic_fh_to_dentry(sb, fid, fh_len, fh_type, - f2fs_nfs_get_inode); -} - -static struct dentry *f2fs_fh_to_parent(struct super_block *sb, struct fid *fid, - int fh_len, int fh_type) -{ - return generic_fh_to_parent(sb, fid, fh_len, fh_type, - f2fs_nfs_get_inode); -} - -static const struct export_operations f2fs_export_ops = { - .fh_to_dentry = f2fs_fh_to_dentry, - .fh_to_parent = f2fs_fh_to_parent, - .get_parent = f2fs_get_parent, -}; - -static int parse_options(struct f2fs_sb_info *sbi, char *options) -{ - substring_t args[MAX_OPT_ARGS]; - char *p; - int arg = 0; - - if (!options) - return 0; - - while ((p = strsep(&options, ",")) != NULL) { - int token; - if (!*p) - continue; - /* - * Initialize args struct so we know whether arg was - * found; some options take optional arguments. - */ - args[0].to = args[0].from = NULL; - token = match_token(p, f2fs_tokens, args); - - switch (token) { - case Opt_gc_background_off: - clear_opt(sbi, BG_GC); - break; - case Opt_disable_roll_forward: - set_opt(sbi, DISABLE_ROLL_FORWARD); - break; - case Opt_discard: - set_opt(sbi, DISCARD); - break; - case Opt_noheap: - set_opt(sbi, NOHEAP); - break; -#ifdef CONFIG_F2FS_FS_XATTR - case Opt_nouser_xattr: - clear_opt(sbi, XATTR_USER); - break; -#else - case Opt_nouser_xattr: - pr_info("nouser_xattr options not supported\n"); - break; -#endif -#ifdef CONFIG_F2FS_FS_POSIX_ACL - case Opt_noacl: - clear_opt(sbi, POSIX_ACL); - break; -#else - case Opt_noacl: - pr_info("noacl options not supported\n"); - break; -#endif - case Opt_active_logs: - if (args->from && match_int(args, &arg)) - return -EINVAL; - if (arg != 2 && arg != 4 && arg != 6) - return -EINVAL; - sbi->active_logs = arg; - break; - case Opt_disable_ext_identify: - set_opt(sbi, DISABLE_EXT_IDENTIFY); - break; - default: - pr_err("Unrecognized mount option \"%s\" or missing value\n", - p); - return -EINVAL; - } - } - return 0; -} - -static loff_t max_file_size(unsigned bits) -{ - loff_t result = ADDRS_PER_INODE; - loff_t leaf_count = ADDRS_PER_BLOCK; - - /* two direct node blocks */ - result += (leaf_count * 2); - - /* two indirect node blocks */ - leaf_count *= NIDS_PER_BLOCK; - result += (leaf_count * 2); - - /* one double indirect node block */ - leaf_count *= NIDS_PER_BLOCK; - result += leaf_count; - - result <<= bits; - return result; -} - -static int sanity_check_raw_super(struct f2fs_super_block *raw_super) -{ - unsigned int blocksize; - - if (F2FS_SUPER_MAGIC != le32_to_cpu(raw_super->magic)) - return 1; - - /* Currently, support only 4KB block size */ - blocksize = 1 << le32_to_cpu(raw_super->log_blocksize); - if (blocksize != PAGE_CACHE_SIZE) - return 1; - if (le32_to_cpu(raw_super->log_sectorsize) != - F2FS_LOG_SECTOR_SIZE) - return 1; - if (le32_to_cpu(raw_super->log_sectors_per_block) != - F2FS_LOG_SECTORS_PER_BLOCK) - return 1; - return 0; -} - -static int sanity_check_ckpt(struct f2fs_super_block *raw_super, - struct f2fs_checkpoint *ckpt) -{ - unsigned int total, fsmeta; - - total = le32_to_cpu(raw_super->segment_count); - fsmeta = le32_to_cpu(raw_super->segment_count_ckpt); - fsmeta += le32_to_cpu(raw_super->segment_count_sit); - fsmeta += le32_to_cpu(raw_super->segment_count_nat); - fsmeta += le32_to_cpu(ckpt->rsvd_segment_count); - fsmeta += le32_to_cpu(raw_super->segment_count_ssa); - - if (fsmeta >= total) - return 1; - return 0; -} - -static void init_sb_info(struct f2fs_sb_info *sbi) -{ - struct f2fs_super_block *raw_super = sbi->raw_super; - int i; - - sbi->log_sectors_per_block = - le32_to_cpu(raw_super->log_sectors_per_block); - sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize); - sbi->blocksize = 1 << sbi->log_blocksize; - sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg); - sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg; - sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec); - sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone); - sbi->total_sections = le32_to_cpu(raw_super->section_count); - sbi->total_node_count = - (le32_to_cpu(raw_super->segment_count_nat) / 2) - * sbi->blocks_per_seg * NAT_ENTRY_PER_BLOCK; - sbi->root_ino_num = le32_to_cpu(raw_super->root_ino); - sbi->node_ino_num = le32_to_cpu(raw_super->node_ino); - sbi->meta_ino_num = le32_to_cpu(raw_super->meta_ino); - - for (i = 0; i < NR_COUNT_TYPE; i++) - atomic_set(&sbi->nr_pages[i], 0); -} - -static int f2fs_fill_super(struct super_block *sb, void *data, int silent) -{ - struct f2fs_sb_info *sbi; - struct f2fs_super_block *raw_super; - struct buffer_head *raw_super_buf; - struct inode *root; - long err = -EINVAL; - int i; - - /* allocate memory for f2fs-specific super block info */ - sbi = kzalloc(sizeof(struct f2fs_sb_info), GFP_KERNEL); - if (!sbi) - return -ENOMEM; - - /* set a temporary block size */ - if (!sb_set_blocksize(sb, F2FS_BLKSIZE)) - goto free_sbi; - - /* read f2fs raw super block */ - raw_super_buf = sb_bread(sb, 0); - if (!raw_super_buf) { - err = -EIO; - goto free_sbi; - } - raw_super = (struct f2fs_super_block *) - ((char *)raw_super_buf->b_data + F2FS_SUPER_OFFSET); - - /* init some FS parameters */ - sbi->active_logs = NR_CURSEG_TYPE; - - set_opt(sbi, BG_GC); - -#ifdef CONFIG_F2FS_FS_XATTR - set_opt(sbi, XATTR_USER); -#endif -#ifdef CONFIG_F2FS_FS_POSIX_ACL - set_opt(sbi, POSIX_ACL); -#endif - /* parse mount options */ - if (parse_options(sbi, (char *)data)) - goto free_sb_buf; - - /* sanity checking of raw super */ - if (sanity_check_raw_super(raw_super)) - goto free_sb_buf; - - sb->s_maxbytes = max_file_size(le32_to_cpu(raw_super->log_blocksize)); - sb->s_max_links = F2FS_LINK_MAX; - get_random_bytes(&sbi->s_next_generation, sizeof(u32)); - - sb->s_op = &f2fs_sops; - sb->s_xattr = f2fs_xattr_handlers; - sb->s_export_op = &f2fs_export_ops; - sb->s_magic = F2FS_SUPER_MAGIC; - sb->s_fs_info = sbi; - sb->s_time_gran = 1; - sb->s_flags = (sb->s_flags & ~MS_POSIXACL) | - (test_opt(sbi, POSIX_ACL) ? MS_POSIXACL : 0); - memcpy(sb->s_uuid, raw_super->uuid, sizeof(raw_super->uuid)); - - /* init f2fs-specific super block info */ - sbi->sb = sb; - sbi->raw_super = raw_super; - sbi->raw_super_buf = raw_super_buf; - mutex_init(&sbi->gc_mutex); - mutex_init(&sbi->write_inode); - mutex_init(&sbi->writepages); - mutex_init(&sbi->cp_mutex); - for (i = 0; i < NR_LOCK_TYPE; i++) - mutex_init(&sbi->fs_lock[i]); - sbi->por_doing = 0; - spin_lock_init(&sbi->stat_lock); - init_rwsem(&sbi->bio_sem); - init_sb_info(sbi); - - /* get an inode for meta space */ - sbi->meta_inode = f2fs_iget(sb, F2FS_META_INO(sbi)); - if (IS_ERR(sbi->meta_inode)) { - err = PTR_ERR(sbi->meta_inode); - goto free_sb_buf; - } - - err = get_valid_checkpoint(sbi); - if (err) - goto free_meta_inode; - - /* sanity checking of checkpoint */ - err = -EINVAL; - if (sanity_check_ckpt(raw_super, sbi->ckpt)) - goto free_cp; - - sbi->total_valid_node_count = - le32_to_cpu(sbi->ckpt->valid_node_count); - sbi->total_valid_inode_count = - le32_to_cpu(sbi->ckpt->valid_inode_count); - sbi->user_block_count = le64_to_cpu(sbi->ckpt->user_block_count); - sbi->total_valid_block_count = - le64_to_cpu(sbi->ckpt->valid_block_count); - sbi->last_valid_block_count = sbi->total_valid_block_count; - sbi->alloc_valid_block_count = 0; - INIT_LIST_HEAD(&sbi->dir_inode_list); - spin_lock_init(&sbi->dir_inode_lock); - - /* init super block */ - if (!sb_set_blocksize(sb, sbi->blocksize)) - goto free_cp; - - init_orphan_info(sbi); - - /* setup f2fs internal modules */ - err = build_segment_manager(sbi); - if (err) - goto free_sm; - err = build_node_manager(sbi); - if (err) - goto free_nm; - - build_gc_manager(sbi); - - /* get an inode for node space */ - sbi->node_inode = f2fs_iget(sb, F2FS_NODE_INO(sbi)); - if (IS_ERR(sbi->node_inode)) { - err = PTR_ERR(sbi->node_inode); - goto free_nm; - } - - /* if there are nt orphan nodes free them */ - err = -EINVAL; - if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) && - recover_orphan_inodes(sbi)) - goto free_node_inode; - - /* read root inode and dentry */ - root = f2fs_iget(sb, F2FS_ROOT_INO(sbi)); - if (IS_ERR(root)) { - err = PTR_ERR(root); - goto free_node_inode; - } - if (!S_ISDIR(root->i_mode) || !root->i_blocks || !root->i_size) - goto free_root_inode; - - sb->s_root = d_make_root(root); /* allocate root dentry */ - if (!sb->s_root) { - err = -ENOMEM; - goto free_root_inode; - } - - /* recover fsynced data */ - if (!is_set_ckpt_flags(F2FS_CKPT(sbi), CP_UMOUNT_FLAG) && - !test_opt(sbi, DISABLE_ROLL_FORWARD)) - recover_fsync_data(sbi); - - /* After POR, we can run background GC thread */ - err = start_gc_thread(sbi); - if (err) - goto fail; - - err = f2fs_build_stats(sbi); - if (err) - goto fail; - - return 0; -fail: - stop_gc_thread(sbi); -free_root_inode: - dput(sb->s_root); - sb->s_root = NULL; -free_node_inode: - iput(sbi->node_inode); -free_nm: - destroy_node_manager(sbi); -free_sm: - destroy_segment_manager(sbi); -free_cp: - kfree(sbi->ckpt); -free_meta_inode: - make_bad_inode(sbi->meta_inode); - iput(sbi->meta_inode); -free_sb_buf: - brelse(raw_super_buf); -free_sbi: - kfree(sbi); - return err; -} - -static struct dentry *f2fs_mount(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) -{ - return mount_bdev(fs_type, flags, dev_name, data, f2fs_fill_super); -} - -static struct file_system_type f2fs_fs_type = { - .owner = THIS_MODULE, - .name = "f2fs", - .mount = f2fs_mount, - .kill_sb = kill_block_super, - .fs_flags = FS_REQUIRES_DEV, -}; - -static int init_inodecache(void) -{ - f2fs_inode_cachep = f2fs_kmem_cache_create("f2fs_inode_cache", - sizeof(struct f2fs_inode_info), NULL); - if (f2fs_inode_cachep == NULL) - return -ENOMEM; - return 0; -} - -static void destroy_inodecache(void) -{ - /* - * Make sure all delayed rcu free inodes are flushed before we - * destroy cache. - */ - rcu_barrier(); - kmem_cache_destroy(f2fs_inode_cachep); -} - -static int __init init_f2fs_fs(void) -{ - int err; - - err = init_inodecache(); - if (err) - goto fail; - err = create_node_manager_caches(); - if (err) - goto fail; - err = create_gc_caches(); - if (err) - goto fail; - err = create_checkpoint_caches(); - if (err) - goto fail; - return register_filesystem(&f2fs_fs_type); -fail: - return err; -} - -static void __exit exit_f2fs_fs(void) -{ - destroy_root_stats(); - unregister_filesystem(&f2fs_fs_type); - destroy_checkpoint_caches(); - destroy_gc_caches(); - destroy_node_manager_caches(); - destroy_inodecache(); -} - -module_init(init_f2fs_fs) -module_exit(exit_f2fs_fs) - -MODULE_AUTHOR("Samsung Electronics's Praesto Team"); -MODULE_DESCRIPTION("Flash Friendly File System"); -MODULE_LICENSE("GPL"); diff --git a/trunk/fs/f2fs/xattr.c b/trunk/fs/f2fs/xattr.c deleted file mode 100644 index 7d52e8dc0c59..000000000000 --- a/trunk/fs/f2fs/xattr.c +++ /dev/null @@ -1,440 +0,0 @@ -/* - * fs/f2fs/xattr.c - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Portions of this code from linux/fs/ext2/xattr.c - * - * Copyright (C) 2001-2003 Andreas Gruenbacher - * - * Fix by Harrison Xing . - * Extended attributes for symlinks and special files added per - * suggestion of Luka Renko . - * xattr consolidation Copyright (c) 2004 James Morris , - * Red Hat Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include -#include -#include "f2fs.h" -#include "xattr.h" - -static size_t f2fs_xattr_generic_list(struct dentry *dentry, char *list, - size_t list_size, const char *name, size_t name_len, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - int total_len, prefix_len = 0; - const char *prefix = NULL; - - switch (type) { - case F2FS_XATTR_INDEX_USER: - if (!test_opt(sbi, XATTR_USER)) - return -EOPNOTSUPP; - prefix = XATTR_USER_PREFIX; - prefix_len = XATTR_USER_PREFIX_LEN; - break; - case F2FS_XATTR_INDEX_TRUSTED: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - prefix = XATTR_TRUSTED_PREFIX; - prefix_len = XATTR_TRUSTED_PREFIX_LEN; - break; - default: - return -EINVAL; - } - - total_len = prefix_len + name_len + 1; - if (list && total_len <= list_size) { - memcpy(list, prefix, prefix_len); - memcpy(list+prefix_len, name, name_len); - list[prefix_len + name_len] = '\0'; - } - return total_len; -} - -static int f2fs_xattr_generic_get(struct dentry *dentry, const char *name, - void *buffer, size_t size, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - - switch (type) { - case F2FS_XATTR_INDEX_USER: - if (!test_opt(sbi, XATTR_USER)) - return -EOPNOTSUPP; - break; - case F2FS_XATTR_INDEX_TRUSTED: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - break; - default: - return -EINVAL; - } - if (strcmp(name, "") == 0) - return -EINVAL; - return f2fs_getxattr(dentry->d_inode, type, name, - buffer, size); -} - -static int f2fs_xattr_generic_set(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags, int type) -{ - struct f2fs_sb_info *sbi = F2FS_SB(dentry->d_sb); - - switch (type) { - case F2FS_XATTR_INDEX_USER: - if (!test_opt(sbi, XATTR_USER)) - return -EOPNOTSUPP; - break; - case F2FS_XATTR_INDEX_TRUSTED: - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - break; - default: - return -EINVAL; - } - if (strcmp(name, "") == 0) - return -EINVAL; - - return f2fs_setxattr(dentry->d_inode, type, name, value, size); -} - -static size_t f2fs_xattr_advise_list(struct dentry *dentry, char *list, - size_t list_size, const char *name, size_t name_len, int type) -{ - const char *xname = F2FS_SYSTEM_ADVISE_PREFIX; - size_t size; - - if (type != F2FS_XATTR_INDEX_ADVISE) - return 0; - - size = strlen(xname) + 1; - if (list && size <= list_size) - memcpy(list, xname, size); - return size; -} - -static int f2fs_xattr_advise_get(struct dentry *dentry, const char *name, - void *buffer, size_t size, int type) -{ - struct inode *inode = dentry->d_inode; - - if (strcmp(name, "") != 0) - return -EINVAL; - - *((char *)buffer) = F2FS_I(inode)->i_advise; - return sizeof(char); -} - -static int f2fs_xattr_advise_set(struct dentry *dentry, const char *name, - const void *value, size_t size, int flags, int type) -{ - struct inode *inode = dentry->d_inode; - - if (strcmp(name, "") != 0) - return -EINVAL; - if (!inode_owner_or_capable(inode)) - return -EPERM; - if (value == NULL) - return -EINVAL; - - F2FS_I(inode)->i_advise |= *(char *)value; - return 0; -} - -const struct xattr_handler f2fs_xattr_user_handler = { - .prefix = XATTR_USER_PREFIX, - .flags = F2FS_XATTR_INDEX_USER, - .list = f2fs_xattr_generic_list, - .get = f2fs_xattr_generic_get, - .set = f2fs_xattr_generic_set, -}; - -const struct xattr_handler f2fs_xattr_trusted_handler = { - .prefix = XATTR_TRUSTED_PREFIX, - .flags = F2FS_XATTR_INDEX_TRUSTED, - .list = f2fs_xattr_generic_list, - .get = f2fs_xattr_generic_get, - .set = f2fs_xattr_generic_set, -}; - -const struct xattr_handler f2fs_xattr_advise_handler = { - .prefix = F2FS_SYSTEM_ADVISE_PREFIX, - .flags = F2FS_XATTR_INDEX_ADVISE, - .list = f2fs_xattr_advise_list, - .get = f2fs_xattr_advise_get, - .set = f2fs_xattr_advise_set, -}; - -static const struct xattr_handler *f2fs_xattr_handler_map[] = { - [F2FS_XATTR_INDEX_USER] = &f2fs_xattr_user_handler, -#ifdef CONFIG_F2FS_FS_POSIX_ACL - [F2FS_XATTR_INDEX_POSIX_ACL_ACCESS] = &f2fs_xattr_acl_access_handler, - [F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT] = &f2fs_xattr_acl_default_handler, -#endif - [F2FS_XATTR_INDEX_TRUSTED] = &f2fs_xattr_trusted_handler, - [F2FS_XATTR_INDEX_ADVISE] = &f2fs_xattr_advise_handler, -}; - -const struct xattr_handler *f2fs_xattr_handlers[] = { - &f2fs_xattr_user_handler, -#ifdef CONFIG_F2FS_FS_POSIX_ACL - &f2fs_xattr_acl_access_handler, - &f2fs_xattr_acl_default_handler, -#endif - &f2fs_xattr_trusted_handler, - &f2fs_xattr_advise_handler, - NULL, -}; - -static inline const struct xattr_handler *f2fs_xattr_handler(int name_index) -{ - const struct xattr_handler *handler = NULL; - - if (name_index > 0 && name_index < ARRAY_SIZE(f2fs_xattr_handler_map)) - handler = f2fs_xattr_handler_map[name_index]; - return handler; -} - -int f2fs_getxattr(struct inode *inode, int name_index, const char *name, - void *buffer, size_t buffer_size) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_xattr_entry *entry; - struct page *page; - void *base_addr; - int error = 0, found = 0; - int value_len, name_len; - - if (name == NULL) - return -EINVAL; - name_len = strlen(name); - - if (!fi->i_xattr_nid) - return -ENODATA; - - page = get_node_page(sbi, fi->i_xattr_nid); - base_addr = page_address(page); - - list_for_each_xattr(entry, base_addr) { - if (entry->e_name_index != name_index) - continue; - if (entry->e_name_len != name_len) - continue; - if (!memcmp(entry->e_name, name, name_len)) { - found = 1; - break; - } - } - if (!found) { - error = -ENODATA; - goto cleanup; - } - - value_len = le16_to_cpu(entry->e_value_size); - - if (buffer && value_len > buffer_size) { - error = -ERANGE; - goto cleanup; - } - - if (buffer) { - char *pval = entry->e_name + entry->e_name_len; - memcpy(buffer, pval, value_len); - } - error = value_len; - -cleanup: - f2fs_put_page(page, 1); - return error; -} - -ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, size_t buffer_size) -{ - struct inode *inode = dentry->d_inode; - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_xattr_entry *entry; - struct page *page; - void *base_addr; - int error = 0; - size_t rest = buffer_size; - - if (!fi->i_xattr_nid) - return 0; - - page = get_node_page(sbi, fi->i_xattr_nid); - base_addr = page_address(page); - - list_for_each_xattr(entry, base_addr) { - const struct xattr_handler *handler = - f2fs_xattr_handler(entry->e_name_index); - size_t size; - - if (!handler) - continue; - - size = handler->list(dentry, buffer, rest, entry->e_name, - entry->e_name_len, handler->flags); - if (buffer && size > rest) { - error = -ERANGE; - goto cleanup; - } - - if (buffer) - buffer += size; - rest -= size; - } - error = buffer_size - rest; -cleanup: - f2fs_put_page(page, 1); - return error; -} - -int f2fs_setxattr(struct inode *inode, int name_index, const char *name, - const void *value, size_t value_len) -{ - struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); - struct f2fs_inode_info *fi = F2FS_I(inode); - struct f2fs_xattr_header *header = NULL; - struct f2fs_xattr_entry *here, *last; - struct page *page; - void *base_addr; - int error, found, free, name_len, newsize; - char *pval; - - if (name == NULL) - return -EINVAL; - name_len = strlen(name); - - if (value == NULL) - value_len = 0; - - if (name_len > 255 || value_len > MAX_VALUE_LEN) - return -ERANGE; - - mutex_lock_op(sbi, NODE_NEW); - if (!fi->i_xattr_nid) { - /* Allocate new attribute block */ - struct dnode_of_data dn; - - if (!alloc_nid(sbi, &fi->i_xattr_nid)) { - mutex_unlock_op(sbi, NODE_NEW); - return -ENOSPC; - } - set_new_dnode(&dn, inode, NULL, NULL, fi->i_xattr_nid); - mark_inode_dirty(inode); - - page = new_node_page(&dn, XATTR_NODE_OFFSET); - if (IS_ERR(page)) { - alloc_nid_failed(sbi, fi->i_xattr_nid); - fi->i_xattr_nid = 0; - mutex_unlock_op(sbi, NODE_NEW); - return PTR_ERR(page); - } - - alloc_nid_done(sbi, fi->i_xattr_nid); - base_addr = page_address(page); - header = XATTR_HDR(base_addr); - header->h_magic = cpu_to_le32(F2FS_XATTR_MAGIC); - header->h_refcount = cpu_to_le32(1); - } else { - /* The inode already has an extended attribute block. */ - page = get_node_page(sbi, fi->i_xattr_nid); - if (IS_ERR(page)) { - mutex_unlock_op(sbi, NODE_NEW); - return PTR_ERR(page); - } - - base_addr = page_address(page); - header = XATTR_HDR(base_addr); - } - - if (le32_to_cpu(header->h_magic) != F2FS_XATTR_MAGIC) { - error = -EIO; - goto cleanup; - } - - /* find entry with wanted name. */ - found = 0; - list_for_each_xattr(here, base_addr) { - if (here->e_name_index != name_index) - continue; - if (here->e_name_len != name_len) - continue; - if (!memcmp(here->e_name, name, name_len)) { - found = 1; - break; - } - } - - last = here; - - while (!IS_XATTR_LAST_ENTRY(last)) - last = XATTR_NEXT_ENTRY(last); - - newsize = XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + - name_len + value_len); - - /* 1. Check space */ - if (value) { - /* If value is NULL, it is remove operation. - * In case of update operation, we caculate free. - */ - free = MIN_OFFSET - ((char *)last - (char *)header); - if (found) - free = free - ENTRY_SIZE(here); - - if (free < newsize) { - error = -ENOSPC; - goto cleanup; - } - } - - /* 2. Remove old entry */ - if (found) { - /* If entry is found, remove old entry. - * If not found, remove operation is not needed. - */ - struct f2fs_xattr_entry *next = XATTR_NEXT_ENTRY(here); - int oldsize = ENTRY_SIZE(here); - - memmove(here, next, (char *)last - (char *)next); - last = (struct f2fs_xattr_entry *)((char *)last - oldsize); - memset(last, 0, oldsize); - } - - /* 3. Write new entry */ - if (value) { - /* Before we come here, old entry is removed. - * We just write new entry. */ - memset(last, 0, newsize); - last->e_name_index = name_index; - last->e_name_len = name_len; - memcpy(last->e_name, name, name_len); - pval = last->e_name + name_len; - memcpy(pval, value, value_len); - last->e_value_size = cpu_to_le16(value_len); - } - - set_page_dirty(page); - f2fs_put_page(page, 1); - - if (is_inode_flag_set(fi, FI_ACL_MODE)) { - inode->i_mode = fi->i_acl_mode; - inode->i_ctime = CURRENT_TIME; - clear_inode_flag(fi, FI_ACL_MODE); - } - f2fs_write_inode(inode, NULL); - mutex_unlock_op(sbi, NODE_NEW); - - return 0; -cleanup: - f2fs_put_page(page, 1); - mutex_unlock_op(sbi, NODE_NEW); - return error; -} diff --git a/trunk/fs/f2fs/xattr.h b/trunk/fs/f2fs/xattr.h deleted file mode 100644 index 49c9558305e3..000000000000 --- a/trunk/fs/f2fs/xattr.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * fs/f2fs/xattr.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * Portions of this code from linux/fs/ext2/xattr.h - * - * On-disk format of extended attributes for the ext2 filesystem. - * - * (C) 2001 Andreas Gruenbacher, - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef __F2FS_XATTR_H__ -#define __F2FS_XATTR_H__ - -#include -#include - -/* Magic value in attribute blocks */ -#define F2FS_XATTR_MAGIC 0xF2F52011 - -/* Maximum number of references to one attribute block */ -#define F2FS_XATTR_REFCOUNT_MAX 1024 - -/* Name indexes */ -#define F2FS_SYSTEM_ADVISE_PREFIX "system.advise" -#define F2FS_XATTR_INDEX_USER 1 -#define F2FS_XATTR_INDEX_POSIX_ACL_ACCESS 2 -#define F2FS_XATTR_INDEX_POSIX_ACL_DEFAULT 3 -#define F2FS_XATTR_INDEX_TRUSTED 4 -#define F2FS_XATTR_INDEX_LUSTRE 5 -#define F2FS_XATTR_INDEX_SECURITY 6 -#define F2FS_XATTR_INDEX_ADVISE 7 - -struct f2fs_xattr_header { - __le32 h_magic; /* magic number for identification */ - __le32 h_refcount; /* reference count */ - __u32 h_reserved[4]; /* zero right now */ -}; - -struct f2fs_xattr_entry { - __u8 e_name_index; - __u8 e_name_len; - __le16 e_value_size; /* size of attribute value */ - char e_name[0]; /* attribute name */ -}; - -#define XATTR_HDR(ptr) ((struct f2fs_xattr_header *)(ptr)) -#define XATTR_ENTRY(ptr) ((struct f2fs_xattr_entry *)(ptr)) -#define XATTR_FIRST_ENTRY(ptr) (XATTR_ENTRY(XATTR_HDR(ptr)+1)) -#define XATTR_ROUND (3) - -#define XATTR_ALIGN(size) ((size + XATTR_ROUND) & ~XATTR_ROUND) - -#define ENTRY_SIZE(entry) (XATTR_ALIGN(sizeof(struct f2fs_xattr_entry) + \ - entry->e_name_len + le16_to_cpu(entry->e_value_size))) - -#define XATTR_NEXT_ENTRY(entry) ((struct f2fs_xattr_entry *)((char *)(entry) +\ - ENTRY_SIZE(entry))) - -#define IS_XATTR_LAST_ENTRY(entry) (*(__u32 *)(entry) == 0) - -#define list_for_each_xattr(entry, addr) \ - for (entry = XATTR_FIRST_ENTRY(addr);\ - !IS_XATTR_LAST_ENTRY(entry);\ - entry = XATTR_NEXT_ENTRY(entry)) - - -#define MIN_OFFSET XATTR_ALIGN(PAGE_SIZE - \ - sizeof(struct node_footer) - \ - sizeof(__u32)) - -#define MAX_VALUE_LEN (MIN_OFFSET - sizeof(struct f2fs_xattr_header) - \ - sizeof(struct f2fs_xattr_entry)) - -/* - * On-disk structure of f2fs_xattr - * We use only 1 block for xattr. - * - * +--------------------+ - * | f2fs_xattr_header | - * | | - * +--------------------+ - * | f2fs_xattr_entry | - * | .e_name_index = 1 | - * | .e_name_len = 3 | - * | .e_value_size = 14 | - * | .e_name = "foo" | - * | "value_of_xattr" |<- value_offs = e_name + e_name_len - * +--------------------+ - * | f2fs_xattr_entry | - * | .e_name_index = 4 | - * | .e_name = "bar" | - * +--------------------+ - * | | - * | Free | - * | | - * +--------------------+<- MIN_OFFSET - * | node_footer | - * | (nid, ino, offset) | - * +--------------------+ - * - **/ - -#ifdef CONFIG_F2FS_FS_XATTR -extern const struct xattr_handler f2fs_xattr_user_handler; -extern const struct xattr_handler f2fs_xattr_trusted_handler; -extern const struct xattr_handler f2fs_xattr_acl_access_handler; -extern const struct xattr_handler f2fs_xattr_acl_default_handler; -extern const struct xattr_handler f2fs_xattr_advise_handler; - -extern const struct xattr_handler *f2fs_xattr_handlers[]; - -extern int f2fs_setxattr(struct inode *inode, int name_index, const char *name, - const void *value, size_t value_len); -extern int f2fs_getxattr(struct inode *inode, int name_index, const char *name, - void *buffer, size_t buffer_size); -extern ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, - size_t buffer_size); - -#else - -#define f2fs_xattr_handlers NULL -static inline int f2fs_setxattr(struct inode *inode, int name_index, - const char *name, const void *value, size_t value_len) -{ - return -EOPNOTSUPP; -} -static inline int f2fs_getxattr(struct inode *inode, int name_index, - const char *name, void *buffer, size_t buffer_size) -{ - return -EOPNOTSUPP; -} -static inline ssize_t f2fs_listxattr(struct dentry *dentry, char *buffer, - size_t buffer_size) -{ - return -EOPNOTSUPP; -} -#endif - -#endif /* __F2FS_XATTR_H__ */ diff --git a/trunk/fs/fhandle.c b/trunk/fs/fhandle.c index 999ff5c3cab0..cccdc874bb55 100644 --- a/trunk/fs/fhandle.c +++ b/trunk/fs/fhandle.c @@ -52,7 +52,7 @@ static long do_sys_name_to_handle(struct path *path, handle_bytes = handle_dwords * sizeof(u32); handle->handle_bytes = handle_bytes; if ((handle->handle_bytes > f_handle.handle_bytes) || - (retval == FILEID_INVALID) || (retval == -ENOSPC)) { + (retval == 255) || (retval == -ENOSPC)) { /* As per old exportfs_encode_fh documentation * we could return ENOSPC to indicate overflow * But file system returned 255 always. So handle diff --git a/trunk/fs/nfsd/fault_inject.c b/trunk/fs/nfsd/fault_inject.c index e761ee95617f..e6c38159622f 100644 --- a/trunk/fs/nfsd/fault_inject.c +++ b/trunk/fs/nfsd/fault_inject.c @@ -8,144 +8,61 @@ #include #include #include -#include -#include -#include #include "state.h" -#include "netns.h" +#include "fault_inject.h" struct nfsd_fault_inject_op { char *file; - u64 (*forget)(struct nfs4_client *, u64); - u64 (*print)(struct nfs4_client *, u64); + void (*func)(u64); }; static struct nfsd_fault_inject_op inject_ops[] = { { .file = "forget_clients", - .forget = nfsd_forget_client, - .print = nfsd_print_client, + .func = nfsd_forget_clients, }, { .file = "forget_locks", - .forget = nfsd_forget_client_locks, - .print = nfsd_print_client_locks, + .func = nfsd_forget_locks, }, { .file = "forget_openowners", - .forget = nfsd_forget_client_openowners, - .print = nfsd_print_client_openowners, + .func = nfsd_forget_openowners, }, { .file = "forget_delegations", - .forget = nfsd_forget_client_delegations, - .print = nfsd_print_client_delegations, + .func = nfsd_forget_delegations, }, { .file = "recall_delegations", - .forget = nfsd_recall_client_delegations, - .print = nfsd_print_client_delegations, + .func = nfsd_recall_delegations, }, }; static long int NUM_INJECT_OPS = sizeof(inject_ops) / sizeof(struct nfsd_fault_inject_op); static struct dentry *debug_dir; -static void nfsd_inject_set(struct nfsd_fault_inject_op *op, u64 val) +static int nfsd_inject_set(void *op_ptr, u64 val) { - u64 count = 0; + struct nfsd_fault_inject_op *op = op_ptr; if (val == 0) printk(KERN_INFO "NFSD Fault Injection: %s (all)", op->file); else printk(KERN_INFO "NFSD Fault Injection: %s (n = %llu)", op->file, val); - nfs4_lock_state(); - count = nfsd_for_n_state(val, op->forget); - nfs4_unlock_state(); - printk(KERN_INFO "NFSD: %s: found %llu", op->file, count); -} - -static void nfsd_inject_set_client(struct nfsd_fault_inject_op *op, - struct sockaddr_storage *addr, - size_t addr_size) -{ - char buf[INET6_ADDRSTRLEN]; - struct nfs4_client *clp; - u64 count; - - nfs4_lock_state(); - clp = nfsd_find_client(addr, addr_size); - if (clp) { - count = op->forget(clp, 0); - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); - printk(KERN_INFO "NFSD [%s]: Client %s had %llu state object(s)\n", op->file, buf, count); - } - nfs4_unlock_state(); -} - -static void nfsd_inject_get(struct nfsd_fault_inject_op *op, u64 *val) -{ - nfs4_lock_state(); - *val = nfsd_for_n_state(0, op->print); - nfs4_unlock_state(); -} - -static ssize_t fault_inject_read(struct file *file, char __user *buf, - size_t len, loff_t *ppos) -{ - static u64 val; - char read_buf[25]; - size_t size, ret; - loff_t pos = *ppos; - - if (!pos) - nfsd_inject_get(file->f_dentry->d_inode->i_private, &val); - size = scnprintf(read_buf, sizeof(read_buf), "%llu\n", val); - - if (pos < 0) - return -EINVAL; - if (pos >= size || !len) - return 0; - if (len > size - pos) - len = size - pos; - ret = copy_to_user(buf, read_buf + pos, len); - if (ret == len) - return -EFAULT; - len -= ret; - *ppos = pos + len; - return len; + op->func(val); + return 0; } -static ssize_t fault_inject_write(struct file *file, const char __user *buf, - size_t len, loff_t *ppos) +static int nfsd_inject_get(void *data, u64 *val) { - char write_buf[INET6_ADDRSTRLEN]; - size_t size = min(sizeof(write_buf) - 1, len); - struct net *net = current->nsproxy->net_ns; - struct sockaddr_storage sa; - u64 val; - - if (copy_from_user(write_buf, buf, size)) - return -EFAULT; - write_buf[size] = '\0'; - - size = rpc_pton(net, write_buf, size, (struct sockaddr *)&sa, sizeof(sa)); - if (size > 0) - nfsd_inject_set_client(file->f_dentry->d_inode->i_private, &sa, size); - else { - val = simple_strtoll(write_buf, NULL, 0); - nfsd_inject_set(file->f_dentry->d_inode->i_private, val); - } - return len; /* on success, claim we got the whole input */ + *val = 0; + return 0; } -static const struct file_operations fops_nfsd = { - .owner = THIS_MODULE, - .read = fault_inject_read, - .write = fault_inject_write, -}; +DEFINE_SIMPLE_ATTRIBUTE(fops_nfsd, nfsd_inject_get, nfsd_inject_set, "%llu\n"); void nfsd_fault_inject_cleanup(void) { diff --git a/trunk/fs/nfsd/fault_inject.h b/trunk/fs/nfsd/fault_inject.h new file mode 100644 index 000000000000..90bd0570956c --- /dev/null +++ b/trunk/fs/nfsd/fault_inject.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2011 Bryan Schumaker + * + * Function definitions for fault injection + */ + +#ifndef LINUX_NFSD_FAULT_INJECT_H +#define LINUX_NFSD_FAULT_INJECT_H + +#ifdef CONFIG_NFSD_FAULT_INJECTION +int nfsd_fault_inject_init(void); +void nfsd_fault_inject_cleanup(void); +void nfsd_forget_clients(u64); +void nfsd_forget_locks(u64); +void nfsd_forget_openowners(u64); +void nfsd_forget_delegations(u64); +void nfsd_recall_delegations(u64); +#else /* CONFIG_NFSD_FAULT_INJECTION */ +static inline int nfsd_fault_inject_init(void) { return 0; } +static inline void nfsd_fault_inject_cleanup(void) {} +static inline void nfsd_forget_clients(u64 num) {} +static inline void nfsd_forget_locks(u64 num) {} +static inline void nfsd_forget_openowners(u64 num) {} +static inline void nfsd_forget_delegations(u64 num) {} +static inline void nfsd_recall_delegations(u64 num) {} +#endif /* CONFIG_NFSD_FAULT_INJECTION */ + +#endif /* LINUX_NFSD_FAULT_INJECT_H */ diff --git a/trunk/fs/nfsd/netns.h b/trunk/fs/nfsd/netns.h index 1051bebff1b0..65c2431ea32f 100644 --- a/trunk/fs/nfsd/netns.h +++ b/trunk/fs/nfsd/netns.h @@ -24,18 +24,7 @@ #include #include -/* Hash tables for nfs4_clientid state */ -#define CLIENT_HASH_BITS 4 -#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) -#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) - -#define LOCKOWNER_INO_HASH_BITS 8 -#define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) - -#define SESSION_HASH_SIZE 512 - struct cld_net; -struct nfsd4_client_tracking_ops; struct nfsd_net { struct cld_net *cld_net; @@ -49,62 +38,7 @@ struct nfsd_net { struct lock_manager nfsd4_manager; bool grace_ended; time_t boot_time; - - /* - * reclaim_str_hashtbl[] holds known client info from previous reset/reboot - * used in reboot/reset lease grace period processing - * - * conf_id_hashtbl[], and conf_name_tree hold confirmed - * setclientid_confirmed info. - * - * unconf_str_hastbl[] and unconf_name_tree hold unconfirmed - * setclientid info. - */ - struct list_head *reclaim_str_hashtbl; - int reclaim_str_hashtbl_size; - struct list_head *conf_id_hashtbl; - struct rb_root conf_name_tree; - struct list_head *unconf_id_hashtbl; - struct rb_root unconf_name_tree; - struct list_head *ownerstr_hashtbl; - struct list_head *lockowner_ino_hashtbl; - struct list_head *sessionid_hashtbl; - /* - * client_lru holds client queue ordered by nfs4_client.cl_time - * for lease renewal. - * - * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time - * for last close replay. - * - * All of the above fields are protected by the client_mutex. - */ - struct list_head client_lru; - struct list_head close_lru; - - struct delayed_work laundromat_work; - - /* client_lock protects the client lru list and session hash table */ - spinlock_t client_lock; - - struct file *rec_file; - bool in_grace; - struct nfsd4_client_tracking_ops *client_tracking_ops; - - time_t nfsd4_lease; - time_t nfsd4_grace; - - bool nfsd_net_up; - - /* - * Time of server startup - */ - struct timeval nfssvc_boot; - - struct svc_serv *nfsd_serv; }; -/* Simple check to find out if a given net was properly initialized */ -#define nfsd_netns_ready(nn) ((nn)->sessionid_hashtbl) - extern int nfsd_net_id; #endif /* __NFSD_NETNS_H__ */ diff --git a/trunk/fs/nfsd/nfs2acl.c b/trunk/fs/nfsd/nfs2acl.c index 9170861c804a..b314888825d5 100644 --- a/trunk/fs/nfsd/nfs2acl.c +++ b/trunk/fs/nfsd/nfs2acl.c @@ -253,7 +253,7 @@ static int nfsaclsvc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, (resp->mask & NFS_ACL) ? resp->acl_access : NULL, (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); while (w > 0) { - if (!*(rqstp->rq_next_page++)) + if (!rqstp->rq_respages[rqstp->rq_resused++]) return 0; w -= PAGE_SIZE; } diff --git a/trunk/fs/nfsd/nfs3acl.c b/trunk/fs/nfsd/nfs3acl.c index 9cbc1a841f87..a596e9d987e4 100644 --- a/trunk/fs/nfsd/nfs3acl.c +++ b/trunk/fs/nfsd/nfs3acl.c @@ -184,7 +184,7 @@ static int nfs3svc_encode_getaclres(struct svc_rqst *rqstp, __be32 *p, (resp->mask & NFS_ACL) ? resp->acl_access : NULL, (resp->mask & NFS_DFACL) ? resp->acl_default : NULL); while (w > 0) { - if (!*(rqstp->rq_next_page++)) + if (!rqstp->rq_respages[rqstp->rq_resused++]) return 0; w -= PAGE_SIZE; } diff --git a/trunk/fs/nfsd/nfs3proc.c b/trunk/fs/nfsd/nfs3proc.c index 1fc02dfdc5c4..97d90d1c8608 100644 --- a/trunk/fs/nfsd/nfs3proc.c +++ b/trunk/fs/nfsd/nfs3proc.c @@ -460,7 +460,7 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, __be32 nfserr; int count = 0; loff_t offset; - struct page **p; + int i; caddr_t page_addr = NULL; dprintk("nfsd: READDIR+(3) %s %d bytes at %d\n", @@ -484,8 +484,8 @@ nfsd3_proc_readdirplus(struct svc_rqst *rqstp, struct nfsd3_readdirargs *argp, &resp->common, nfs3svc_encode_entry_plus); memcpy(resp->verf, argp->verf, 8); - for (p = rqstp->rq_respages + 1; p < rqstp->rq_next_page; p++) { - page_addr = page_address(*p); + for (i=1; irq_resused ; i++) { + page_addr = page_address(rqstp->rq_respages[i]); if (((caddr_t)resp->buffer >= page_addr) && ((caddr_t)resp->buffer < page_addr + PAGE_SIZE)) { diff --git a/trunk/fs/nfsd/nfs3xdr.c b/trunk/fs/nfsd/nfs3xdr.c index 324c0baf7cda..43f46cd9edea 100644 --- a/trunk/fs/nfsd/nfs3xdr.c +++ b/trunk/fs/nfsd/nfs3xdr.c @@ -7,10 +7,8 @@ */ #include -#include #include "xdr3.h" #include "auth.h" -#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_XDR @@ -325,7 +323,7 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_readargs *args) { unsigned int len; - int v; + int v,pn; u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) @@ -340,9 +338,8 @@ nfs3svc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, /* set up the kvec */ v=0; while (len > 0) { - struct page *p = *(rqstp->rq_next_page++); - - rqstp->rq_vec[v].iov_base = page_address(p); + pn = rqstp->rq_resused++; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); rqstp->rq_vec[v].iov_len = len < PAGE_SIZE? len : PAGE_SIZE; len -= rqstp->rq_vec[v].iov_len; v++; @@ -464,7 +461,8 @@ nfs3svc_decode_symlinkargs(struct svc_rqst *rqstp, __be32 *p, len = ntohl(*p++); if (len == 0 || len > NFS3_MAXPATHLEN || len >= PAGE_SIZE) return 0; - args->tname = new = page_address(*(rqstp->rq_next_page++)); + args->tname = new = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); args->tlen = len; /* first copy and check from the first page */ old = (char*)p; @@ -535,7 +533,8 @@ nfs3svc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, { if (!(p = decode_fh(p, &args->fh))) return 0; - args->buffer = page_address(*(rqstp->rq_next_page++)); + args->buffer = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -566,7 +565,8 @@ nfs3svc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - args->buffer = page_address(*(rqstp->rq_next_page++)); + args->buffer = + page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -575,7 +575,7 @@ int nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_readdirargs *args) { - int len; + int len, pn; u32 max_blocksize = svc_max_payload(rqstp); if (!(p = decode_fh(p, &args->fh))) @@ -590,9 +590,9 @@ nfs3svc_decode_readdirplusargs(struct svc_rqst *rqstp, __be32 *p, args->count = len; while (len > 0) { - struct page *p = *(rqstp->rq_next_page++); + pn = rqstp->rq_resused++; if (!args->buffer) - args->buffer = page_address(p); + args->buffer = page_address(rqstp->rq_respages[pn]); len -= PAGE_SIZE; } @@ -720,14 +720,12 @@ int nfs3svc_encode_writeres(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_writeres *resp) { - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - p = encode_wcc_data(rqstp, p, &resp->fh); if (resp->status == 0) { *p++ = htonl(resp->count); *p++ = htonl(resp->committed); - *p++ = htonl(nn->nfssvc_boot.tv_sec); - *p++ = htonl(nn->nfssvc_boot.tv_usec); + *p++ = htonl(nfssvc_boot.tv_sec); + *p++ = htonl(nfssvc_boot.tv_usec); } return xdr_ressize_check(rqstp, p); } @@ -878,7 +876,7 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, common); __be32 *p = cd->buffer; caddr_t curr_page_addr = NULL; - struct page ** page; + int pn; /* current page number */ int slen; /* string (name) length */ int elen; /* estimated entry length in words */ int num_entry_words = 0; /* actual number of words */ @@ -915,9 +913,8 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, } /* determine which page in rq_respages[] we are currently filling */ - for (page = cd->rqstp->rq_respages + 1; - page < cd->rqstp->rq_next_page; page++) { - curr_page_addr = page_address(*page); + for (pn=1; pn < cd->rqstp->rq_resused; pn++) { + curr_page_addr = page_address(cd->rqstp->rq_respages[pn]); if (((caddr_t)cd->buffer >= curr_page_addr) && ((caddr_t)cd->buffer < curr_page_addr + PAGE_SIZE)) @@ -932,14 +929,14 @@ encode_entry(struct readdir_cd *ccd, const char *name, int namlen, if (plus) p = encode_entryplus_baggage(cd, p, name, namlen); num_entry_words = p - cd->buffer; - } else if (*(page+1) != NULL) { + } else if (cd->rqstp->rq_respages[pn+1] != NULL) { /* temporarily encode entry into next page, then move back to * current and next page in rq_respages[] */ __be32 *p1, *tmp; int len1, len2; /* grab next page for temporary storage of entry */ - p1 = tmp = page_address(*(page+1)); + p1 = tmp = page_address(cd->rqstp->rq_respages[pn+1]); p1 = encode_entry_baggage(cd, p1, name, namlen, ino); @@ -1085,13 +1082,11 @@ int nfs3svc_encode_commitres(struct svc_rqst *rqstp, __be32 *p, struct nfsd3_commitres *resp) { - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - p = encode_wcc_data(rqstp, p, &resp->fh); /* Write verifier */ if (resp->status == 0) { - *p++ = htonl(nn->nfssvc_boot.tv_sec); - *p++ = htonl(nn->nfssvc_boot.tv_usec); + *p++ = htonl(nfssvc_boot.tv_sec); + *p++ = htonl(nfssvc_boot.tv_usec); } return xdr_ressize_check(rqstp, p); } diff --git a/trunk/fs/nfsd/nfs4callback.c b/trunk/fs/nfsd/nfs4callback.c index 99bc85ff0217..bdf29c96e4cd 100644 --- a/trunk/fs/nfsd/nfs4callback.c +++ b/trunk/fs/nfsd/nfs4callback.c @@ -36,7 +36,6 @@ #include #include "nfsd.h" #include "state.h" -#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -626,46 +625,20 @@ static const struct rpc_program cb_program = { .pipe_dir_name = "nfsd4_cb", }; -static int max_cb_time(struct net *net) +static int max_cb_time(void) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - return max(nn->nfsd4_lease/10, (time_t)1) * HZ; + return max(nfsd4_lease/10, (time_t)1) * HZ; } -static struct rpc_cred *callback_cred; - -int set_callback_cred(void) -{ - if (callback_cred) - return 0; - callback_cred = rpc_lookup_machine_cred("nfs"); - if (!callback_cred) - return -ENOMEM; - return 0; -} - -static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc_clnt *client, struct nfsd4_session *ses) -{ - if (clp->cl_minorversion == 0) { - return get_rpccred(callback_cred); - } else { - struct rpc_auth *auth = client->cl_auth; - struct auth_cred acred = {}; - - acred.uid = ses->se_cb_sec.uid; - acred.gid = ses->se_cb_sec.gid; - return auth->au_ops->lookup_cred(client->cl_auth, &acred, 0); - } -} static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) { struct rpc_timeout timeparms = { - .to_initval = max_cb_time(clp->net), + .to_initval = max_cb_time(), .to_retries = 0, }; struct rpc_create_args args = { - .net = clp->net, + .net = &init_net, .address = (struct sockaddr *) &conn->cb_addr, .addrsize = conn->cb_addrlen, .saddress = (struct sockaddr *) &conn->cb_saddr, @@ -675,7 +648,6 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), }; struct rpc_clnt *client; - struct rpc_cred *cred; if (clp->cl_minorversion == 0) { if (!clp->cl_cred.cr_principal && @@ -694,7 +666,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c args.bc_xprt = conn->cb_xprt; args.prognumber = clp->cl_cb_session->se_cb_prog; args.protocol = XPRT_TRANSPORT_BC_TCP; - args.authflavor = ses->se_cb_sec.flavor; + args.authflavor = RPC_AUTH_UNIX; } /* Create RPC client */ client = rpc_create(&args); @@ -703,14 +675,9 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c PTR_ERR(client)); return PTR_ERR(client); } - cred = get_backchannel_cred(clp, client, ses); - if (IS_ERR(cred)) { - rpc_shutdown_client(client); - return PTR_ERR(cred); - } clp->cl_cb_client = client; - clp->cl_cb_cred = cred; return 0; + } static void warn_no_callback_path(struct nfs4_client *clp, int reason) @@ -747,6 +714,18 @@ static const struct rpc_call_ops nfsd4_cb_probe_ops = { .rpc_call_done = nfsd4_cb_probe_done, }; +static struct rpc_cred *callback_cred; + +int set_callback_cred(void) +{ + if (callback_cred) + return 0; + callback_cred = rpc_lookup_machine_cred("nfs"); + if (!callback_cred) + return -ENOMEM; + return 0; +} + static struct workqueue_struct *callback_wq; static void run_nfsd4_cb(struct nfsd4_callback *cb) @@ -764,6 +743,7 @@ static void do_probe_callback(struct nfs4_client *clp) cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL]; cb->cb_msg.rpc_argp = NULL; cb->cb_msg.rpc_resp = NULL; + cb->cb_msg.rpc_cred = callback_cred; cb->cb_ops = &nfsd4_cb_probe_ops; @@ -982,8 +962,6 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) if (clp->cl_cb_client) { rpc_shutdown_client(clp->cl_cb_client); clp->cl_cb_client = NULL; - put_rpccred(clp->cl_cb_cred); - clp->cl_cb_cred = NULL; } if (clp->cl_cb_conn.cb_xprt) { svc_xprt_put(clp->cl_cb_conn.cb_xprt); @@ -1017,7 +995,7 @@ static void nfsd4_process_cb_update(struct nfsd4_callback *cb) run_nfsd4_cb(cb); } -static void nfsd4_do_callback_rpc(struct work_struct *w) +void nfsd4_do_callback_rpc(struct work_struct *w) { struct nfsd4_callback *cb = container_of(w, struct nfsd4_callback, cb_work); struct nfs4_client *clp = cb->cb_clp; @@ -1032,16 +1010,10 @@ static void nfsd4_do_callback_rpc(struct work_struct *w) nfsd4_release_cb(cb); return; } - cb->cb_msg.rpc_cred = clp->cl_cb_cred; rpc_call_async(clnt, &cb->cb_msg, RPC_TASK_SOFT | RPC_TASK_SOFTCONN, cb->cb_ops, cb); } -void nfsd4_init_callback(struct nfsd4_callback *cb) -{ - INIT_WORK(&cb->cb_work, nfsd4_do_callback_rpc); -} - void nfsd4_cb_recall(struct nfs4_delegation *dp) { struct nfsd4_callback *cb = &dp->dl_recall; @@ -1053,6 +1025,7 @@ void nfsd4_cb_recall(struct nfs4_delegation *dp) cb->cb_msg.rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL]; cb->cb_msg.rpc_argp = cb; cb->cb_msg.rpc_resp = cb; + cb->cb_msg.rpc_cred = callback_cred; cb->cb_ops = &nfsd4_cb_recall_ops; diff --git a/trunk/fs/nfsd/nfs4proc.c b/trunk/fs/nfsd/nfs4proc.c index 9d1c5dba2bbb..6c9a4b291dba 100644 --- a/trunk/fs/nfsd/nfs4proc.c +++ b/trunk/fs/nfsd/nfs4proc.c @@ -40,7 +40,6 @@ #include "xdr4.h" #include "vfs.h" #include "current_stateid.h" -#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_PROC @@ -195,7 +194,6 @@ static __be32 do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open) { struct svc_fh *resfh; - int accmode; __be32 status; resfh = kmalloc(sizeof(struct svc_fh), GFP_KERNEL); @@ -255,10 +253,9 @@ do_open_lookup(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_o /* set reply cache */ fh_copy_shallow(&open->op_openowner->oo_owner.so_replay.rp_openfh, &resfh->fh_handle); - accmode = NFSD_MAY_NOP; - if (open->op_created) - accmode |= NFSD_MAY_OWNER_OVERRIDE; - status = do_open_permission(rqstp, resfh, open, accmode); + if (!open->op_created) + status = do_open_permission(rqstp, resfh, open, + NFSD_MAY_NOP); set_change_info(&open->op_cinfo, current_fh); fh_dup2(current_fh, resfh); out: @@ -307,8 +304,6 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { __be32 status; struct nfsd4_compoundres *resp; - struct net *net = SVC_NET(rqstp); - struct nfsd_net *nn = net_generic(net, nfsd_net_id); dprintk("NFSD: nfsd4_open filename %.*s op_openowner %p\n", (int)open->op_fname.len, open->op_fname.data, @@ -336,7 +331,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* check seqid for replay. set nfs4_owner */ resp = rqstp->rq_resp; - status = nfsd4_process_open1(&resp->cstate, open, nn); + status = nfsd4_process_open1(&resp->cstate, open); if (status == nfserr_replay_me) { struct nfs4_replay *rp = &open->op_openowner->oo_owner.so_replay; fh_put(&cstate->current_fh); @@ -359,10 +354,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, /* Openowner is now set, so sequence id will get bumped. Now we need * these checks before we do any creates: */ status = nfserr_grace; - if (locks_in_grace(net) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) + if (locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type != NFS4_OPEN_CLAIM_PREVIOUS) goto out; status = nfserr_no_grace; - if (!locks_in_grace(net) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) + if (!locks_in_grace(SVC_NET(rqstp)) && open->op_claim_type == NFS4_OPEN_CLAIM_PREVIOUS) goto out; switch (open->op_claim_type) { @@ -375,9 +370,7 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, break; case NFS4_OPEN_CLAIM_PREVIOUS: open->op_openowner->oo_flags |= NFS4_OO_CONFIRMED; - status = nfs4_check_open_reclaim(&open->op_clientid, - cstate->minorversion, - nn); + status = nfs4_check_open_reclaim(&open->op_clientid, cstate->minorversion); if (status) goto out; case NFS4_OPEN_CLAIM_FH: @@ -497,13 +490,12 @@ nfsd4_access(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, &access->ac_supported); } -static void gen_boot_verifier(nfs4_verifier *verifier, struct net *net) +static void gen_boot_verifier(nfs4_verifier *verifier) { __be32 verf[2]; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - verf[0] = (__be32)nn->nfssvc_boot.tv_sec; - verf[1] = (__be32)nn->nfssvc_boot.tv_usec; + verf[0] = (__be32)nfssvc_boot.tv_sec; + verf[1] = (__be32)nfssvc_boot.tv_usec; memcpy(verifier->data, verf, sizeof(verifier->data)); } @@ -511,7 +503,7 @@ static __be32 nfsd4_commit(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_commit *commit) { - gen_boot_verifier(&commit->co_verf, SVC_NET(rqstp)); + gen_boot_verifier(&commit->co_verf); return nfsd_commit(rqstp, &cstate->current_fh, commit->co_offset, commit->co_count); } @@ -692,17 +684,6 @@ nfsd4_read(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, if (read->rd_offset >= OFFSET_MAX) return nfserr_inval; - /* - * If we do a zero copy read, then a client will see read data - * that reflects the state of the file *after* performing the - * following compound. - * - * To ensure proper ordering, we therefore turn off zero copy if - * the client wants us to do more in this compound: - */ - if (!nfsd4_last_compound_op(rqstp)) - rqstp->rq_splice_ok = false; - nfs4_lock_state(); /* check stateid */ if ((status = nfs4_preprocess_stateid_op(SVC_NET(rqstp), @@ -895,24 +876,6 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, return status; } -static int fill_in_write_vector(struct kvec *vec, struct nfsd4_write *write) -{ - int i = 1; - int buflen = write->wr_buflen; - - vec[0].iov_base = write->wr_head.iov_base; - vec[0].iov_len = min_t(int, buflen, write->wr_head.iov_len); - buflen -= vec[0].iov_len; - - while (buflen) { - vec[i].iov_base = page_address(write->wr_pagelist[i - 1]); - vec[i].iov_len = min_t(int, PAGE_SIZE, buflen); - buflen -= vec[i].iov_len; - i++; - } - return i; -} - static __be32 nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_write *write) @@ -921,7 +884,6 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file *filp = NULL; __be32 status = nfs_ok; unsigned long cnt; - int nvecs; /* no need to check permission - this will be done in nfsd_write() */ @@ -942,13 +904,10 @@ nfsd4_write(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, cnt = write->wr_buflen; write->wr_how_written = write->wr_stable_how; - gen_boot_verifier(&write->wr_verifier, SVC_NET(rqstp)); - - nvecs = fill_in_write_vector(rqstp->rq_vec, write); - WARN_ON_ONCE(nvecs > ARRAY_SIZE(rqstp->rq_vec)); + gen_boot_verifier(&write->wr_verifier); status = nfsd_write(rqstp, &cstate->current_fh, filp, - write->wr_offset, rqstp->rq_vec, nvecs, + write->wr_offset, rqstp->rq_vec, write->wr_vlen, &cnt, &write->wr_how_written); if (filp) fput(filp); @@ -1707,12 +1666,6 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_name = "OP_EXCHANGE_ID", .op_rsize_bop = (nfsd4op_rsize)nfsd4_exchange_id_rsize, }, - [OP_BACKCHANNEL_CTL] = { - .op_func = (nfsd4op_func)nfsd4_backchannel_ctl, - .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, - .op_name = "OP_BACKCHANNEL_CTL", - .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, - }, [OP_BIND_CONN_TO_SESSION] = { .op_func = (nfsd4op_func)nfsd4_bind_conn_to_session, .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP @@ -1766,7 +1719,6 @@ static struct nfsd4_operation nfsd4_ops[] = { .op_func = (nfsd4op_func)nfsd4_free_stateid, .op_flags = ALLOWED_WITHOUT_FH | OP_MODIFIES_SOMETHING, .op_name = "OP_FREE_STATEID", - .op_get_currentstateid = (stateid_getter)nfsd4_get_freestateid, .op_rsize_bop = (nfsd4op_rsize)nfsd4_only_status_rsize, }, }; diff --git a/trunk/fs/nfsd/nfs4recover.c b/trunk/fs/nfsd/nfs4recover.c index ba6fdd4a0455..43295d45cc2b 100644 --- a/trunk/fs/nfsd/nfs4recover.c +++ b/trunk/fs/nfsd/nfs4recover.c @@ -58,11 +58,13 @@ struct nfsd4_client_tracking_ops { void (*create)(struct nfs4_client *); void (*remove)(struct nfs4_client *); int (*check)(struct nfs4_client *); - void (*grace_done)(struct nfsd_net *, time_t); + void (*grace_done)(struct net *, time_t); }; /* Globals */ +static struct file *rec_file; static char user_recovery_dirname[PATH_MAX] = "/var/lib/nfs/v4recovery"; +static struct nfsd4_client_tracking_ops *client_tracking_ops; static int nfs4_save_creds(const struct cred **original_creds) @@ -100,39 +102,33 @@ md5_to_hex(char *out, char *md5) *out = '\0'; } -static int -nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname) +__be32 +nfs4_make_rec_clidname(char *dname, struct xdr_netobj *clname) { struct xdr_netobj cksum; struct hash_desc desc; struct scatterlist sg; - int status; + __be32 status = nfserr_jukebox; dprintk("NFSD: nfs4_make_rec_clidname for %.*s\n", clname->len, clname->data); desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP; desc.tfm = crypto_alloc_hash("md5", 0, CRYPTO_ALG_ASYNC); - if (IS_ERR(desc.tfm)) { - status = PTR_ERR(desc.tfm); + if (IS_ERR(desc.tfm)) goto out_no_tfm; - } - cksum.len = crypto_hash_digestsize(desc.tfm); cksum.data = kmalloc(cksum.len, GFP_KERNEL); - if (cksum.data == NULL) { - status = -ENOMEM; + if (cksum.data == NULL) goto out; - } sg_init_one(&sg, clname->data, clname->len); - status = crypto_hash_digest(&desc, &sg, sg.length, cksum.data); - if (status) + if (crypto_hash_digest(&desc, &sg, sg.length, cksum.data)) goto out; md5_to_hex(dname, cksum.data); - status = 0; + status = nfs_ok; out: kfree(cksum.data); crypto_free_hash(desc.tfm); @@ -140,61 +136,29 @@ nfs4_make_rec_clidname(char *dname, const struct xdr_netobj *clname) return status; } -/* - * If we had an error generating the recdir name for the legacy tracker - * then warn the admin. If the error doesn't appear to be transient, - * then disable recovery tracking. - */ -static void -legacy_recdir_name_error(int error) -{ - printk(KERN_ERR "NFSD: unable to generate recoverydir " - "name (%d).\n", error); - - /* - * if the algorithm just doesn't exist, then disable the recovery - * tracker altogether. The crypto libs will generally return this if - * FIPS is enabled as well. - */ - if (error == -ENOENT) { - printk(KERN_ERR "NFSD: disabling legacy clientid tracking. " - "Reboot recovery will not function correctly!\n"); - - /* the argument is ignored by the legacy exit function */ - nfsd4_client_tracking_exit(NULL); - } -} - static void nfsd4_create_clid_dir(struct nfs4_client *clp) { const struct cred *original_cred; - char dname[HEXDIR_LEN]; + char *dname = clp->cl_recdir; struct dentry *dir, *dentry; - struct nfs4_client_reclaim *crp; int status; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); dprintk("NFSD: nfsd4_create_clid_dir for \"%s\"\n", dname); if (test_and_set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return; - if (!nn->rec_file) + if (!rec_file) return; - - status = nfs4_make_rec_clidname(dname, &clp->cl_name); - if (status) - return legacy_recdir_name_error(status); - status = nfs4_save_creds(&original_cred); if (status < 0) return; - status = mnt_want_write_file(nn->rec_file); + status = mnt_want_write_file(rec_file); if (status) return; - dir = nn->rec_file->f_path.dentry; + dir = rec_file->f_path.dentry; /* lock the parent */ mutex_lock(&dir->d_inode->i_mutex); @@ -218,24 +182,18 @@ nfsd4_create_clid_dir(struct nfs4_client *clp) dput(dentry); out_unlock: mutex_unlock(&dir->d_inode->i_mutex); - if (status == 0) { - if (nn->in_grace) { - crp = nfs4_client_to_reclaim(dname, nn); - if (crp) - crp->cr_clp = clp; - } - vfs_fsync(nn->rec_file, 0); - } else { + if (status == 0) + vfs_fsync(rec_file, 0); + else printk(KERN_ERR "NFSD: failed to write recovery record" " (err %d); please check that %s exists" " and is writeable", status, user_recovery_dirname); - } - mnt_drop_write_file(nn->rec_file); + mnt_drop_write_file(rec_file); nfs4_reset_creds(original_cred); } -typedef int (recdir_func)(struct dentry *, struct dentry *, struct nfsd_net *); +typedef int (recdir_func)(struct dentry *, struct dentry *); struct name_list { char name[HEXDIR_LEN]; @@ -261,10 +219,10 @@ nfsd4_build_namelist(void *arg, const char *name, int namlen, } static int -nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) +nfsd4_list_rec_dir(recdir_func *f) { const struct cred *original_cred; - struct dentry *dir = nn->rec_file->f_path.dentry; + struct dentry *dir = rec_file->f_path.dentry; LIST_HEAD(names); int status; @@ -272,13 +230,13 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) if (status < 0) return status; - status = vfs_llseek(nn->rec_file, 0, SEEK_SET); + status = vfs_llseek(rec_file, 0, SEEK_SET); if (status < 0) { nfs4_reset_creds(original_cred); return status; } - status = vfs_readdir(nn->rec_file, nfsd4_build_namelist, &names); + status = vfs_readdir(rec_file, nfsd4_build_namelist, &names); mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); while (!list_empty(&names)) { struct name_list *entry; @@ -290,7 +248,7 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) status = PTR_ERR(dentry); break; } - status = f(dir, dentry, nn); + status = f(dir, dentry); dput(dentry); } list_del(&entry->list); @@ -302,14 +260,14 @@ nfsd4_list_rec_dir(recdir_func *f, struct nfsd_net *nn) } static int -nfsd4_unlink_clid_dir(char *name, int namlen, struct nfsd_net *nn) +nfsd4_unlink_clid_dir(char *name, int namlen) { struct dentry *dir, *dentry; int status; dprintk("NFSD: nfsd4_unlink_clid_dir. name %.*s\n", namlen, name); - dir = nn->rec_file->f_path.dentry; + dir = rec_file->f_path.dentry; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); dentry = lookup_one_len(name, dir, namlen); if (IS_ERR(dentry)) { @@ -331,52 +289,37 @@ static void nfsd4_remove_clid_dir(struct nfs4_client *clp) { const struct cred *original_cred; - struct nfs4_client_reclaim *crp; - char dname[HEXDIR_LEN]; int status; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - if (!nn->rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) + if (!rec_file || !test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return; - status = nfs4_make_rec_clidname(dname, &clp->cl_name); - if (status) - return legacy_recdir_name_error(status); - - status = mnt_want_write_file(nn->rec_file); + status = mnt_want_write_file(rec_file); if (status) goto out; clear_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); status = nfs4_save_creds(&original_cred); if (status < 0) - goto out_drop_write; + goto out; - status = nfsd4_unlink_clid_dir(dname, HEXDIR_LEN-1, nn); + status = nfsd4_unlink_clid_dir(clp->cl_recdir, HEXDIR_LEN-1); nfs4_reset_creds(original_cred); - if (status == 0) { - vfs_fsync(nn->rec_file, 0); - if (nn->in_grace) { - /* remove reclaim record */ - crp = nfsd4_find_reclaim_client(dname, nn); - if (crp) - nfs4_remove_reclaim_record(crp, nn); - } - } -out_drop_write: - mnt_drop_write_file(nn->rec_file); + if (status == 0) + vfs_fsync(rec_file, 0); + mnt_drop_write_file(rec_file); out: if (status) printk("NFSD: Failed to remove expired client state directory" - " %.*s\n", HEXDIR_LEN, dname); + " %.*s\n", HEXDIR_LEN, clp->cl_recdir); } static int -purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) +purge_old(struct dentry *parent, struct dentry *child) { int status; - if (nfs4_has_reclaimed_state(child->d_name.name, nn)) + if (nfs4_has_reclaimed_state(child->d_name.name, false)) return 0; status = vfs_rmdir(parent->d_inode, child); @@ -388,29 +331,27 @@ purge_old(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) } static void -nfsd4_recdir_purge_old(struct nfsd_net *nn, time_t boot_time) +nfsd4_recdir_purge_old(struct net *net, time_t boot_time) { int status; - nn->in_grace = false; - if (!nn->rec_file) + if (!rec_file) return; - status = mnt_want_write_file(nn->rec_file); + status = mnt_want_write_file(rec_file); if (status) goto out; - status = nfsd4_list_rec_dir(purge_old, nn); + status = nfsd4_list_rec_dir(purge_old); if (status == 0) - vfs_fsync(nn->rec_file, 0); - mnt_drop_write_file(nn->rec_file); + vfs_fsync(rec_file, 0); + mnt_drop_write_file(rec_file); out: - nfs4_release_reclaim(nn); if (status) printk("nfsd4: failed to purge old clients from recovery" - " directory %s\n", nn->rec_file->f_path.dentry->d_name.name); + " directory %s\n", rec_file->f_path.dentry->d_name.name); } static int -load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) +load_recdir(struct dentry *parent, struct dentry *child) { if (child->d_name.len != HEXDIR_LEN - 1) { printk("nfsd4: illegal name %s in recovery directory\n", @@ -418,22 +359,21 @@ load_recdir(struct dentry *parent, struct dentry *child, struct nfsd_net *nn) /* Keep trying; maybe the others are OK: */ return 0; } - nfs4_client_to_reclaim(child->d_name.name, nn); + nfs4_client_to_reclaim(child->d_name.name); return 0; } static int -nfsd4_recdir_load(struct net *net) { +nfsd4_recdir_load(void) { int status; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - if (!nn->rec_file) + if (!rec_file) return 0; - status = nfsd4_list_rec_dir(load_recdir, nn); + status = nfsd4_list_rec_dir(load_recdir); if (status) printk("nfsd4: failed loading clients from recovery" - " directory %s\n", nn->rec_file->f_path.dentry->d_name.name); + " directory %s\n", rec_file->f_path.dentry->d_name.name); return status; } @@ -442,16 +382,15 @@ nfsd4_recdir_load(struct net *net) { */ static int -nfsd4_init_recdir(struct net *net) +nfsd4_init_recdir(void) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); const struct cred *original_cred; int status; printk("NFSD: Using %s as the NFSv4 state recovery directory\n", user_recovery_dirname); - BUG_ON(nn->rec_file); + BUG_ON(rec_file); status = nfs4_save_creds(&original_cred); if (status < 0) { @@ -461,65 +400,23 @@ nfsd4_init_recdir(struct net *net) return status; } - nn->rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); - if (IS_ERR(nn->rec_file)) { + rec_file = filp_open(user_recovery_dirname, O_RDONLY | O_DIRECTORY, 0); + if (IS_ERR(rec_file)) { printk("NFSD: unable to find recovery directory %s\n", user_recovery_dirname); - status = PTR_ERR(nn->rec_file); - nn->rec_file = NULL; + status = PTR_ERR(rec_file); + rec_file = NULL; } nfs4_reset_creds(original_cred); - if (!status) - nn->in_grace = true; return status; } - -static int -nfs4_legacy_state_init(struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - int i; - - nn->reclaim_str_hashtbl = kmalloc(sizeof(struct list_head) * - CLIENT_HASH_SIZE, GFP_KERNEL); - if (!nn->reclaim_str_hashtbl) - return -ENOMEM; - - for (i = 0; i < CLIENT_HASH_SIZE; i++) - INIT_LIST_HEAD(&nn->reclaim_str_hashtbl[i]); - nn->reclaim_str_hashtbl_size = 0; - - return 0; -} - -static void -nfs4_legacy_state_shutdown(struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - kfree(nn->reclaim_str_hashtbl); -} - static int nfsd4_load_reboot_recovery_data(struct net *net) { int status; - status = nfsd4_init_recdir(net); - if (!status) - status = nfsd4_recdir_load(net); - if (status) - printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); - return status; -} - -static int -nfsd4_legacy_tracking_init(struct net *net) -{ - int status; - /* XXX: The legacy code won't work in a container */ if (net != &init_net) { WARN(1, KERN_ERR "NFSD: attempt to initialize legacy client " @@ -527,37 +424,30 @@ nfsd4_legacy_tracking_init(struct net *net) return -EINVAL; } - status = nfs4_legacy_state_init(net); - if (status) - return status; - - status = nfsd4_load_reboot_recovery_data(net); + nfs4_lock_state(); + status = nfsd4_init_recdir(); + if (!status) + status = nfsd4_recdir_load(); + nfs4_unlock_state(); if (status) - goto err; - return 0; - -err: - nfs4_legacy_state_shutdown(net); + printk(KERN_ERR "NFSD: Failure reading reboot recovery data\n"); return status; } static void -nfsd4_shutdown_recdir(struct nfsd_net *nn) +nfsd4_shutdown_recdir(void) { - if (!nn->rec_file) + if (!rec_file) return; - fput(nn->rec_file); - nn->rec_file = NULL; + fput(rec_file); + rec_file = NULL; } static void nfsd4_legacy_tracking_exit(struct net *net) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - nfs4_release_reclaim(nn); - nfsd4_shutdown_recdir(nn); - nfs4_legacy_state_shutdown(net); + nfs4_release_reclaim(); + nfsd4_shutdown_recdir(); } /* @@ -590,26 +480,13 @@ nfs4_recoverydir(void) static int nfsd4_check_legacy_client(struct nfs4_client *clp) { - int status; - char dname[HEXDIR_LEN]; - struct nfs4_client_reclaim *crp; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - /* did we already find that this client is stable? */ if (test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags)) return 0; - status = nfs4_make_rec_clidname(dname, &clp->cl_name); - if (status) { - legacy_recdir_name_error(status); - return status; - } - /* look for it in the reclaim hashtable otherwise */ - crp = nfsd4_find_reclaim_client(dname, nn); - if (crp) { + if (nfsd4_find_reclaim_client(clp)) { set_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); - crp->cr_clp = clp; return 0; } @@ -617,7 +494,7 @@ nfsd4_check_legacy_client(struct nfs4_client *clp) } static struct nfsd4_client_tracking_ops nfsd4_legacy_tracking_ops = { - .init = nfsd4_legacy_tracking_init, + .init = nfsd4_load_reboot_recovery_data, .exit = nfsd4_legacy_tracking_exit, .create = nfsd4_create_clid_dir, .remove = nfsd4_remove_clid_dir, @@ -908,7 +785,8 @@ nfsd4_cld_create(struct nfs4_client *clp) { int ret; struct cld_upcall *cup; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + /* FIXME: determine net from clp */ + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); struct cld_net *cn = nn->cld_net; /* Don't upcall if it's already stored */ @@ -945,7 +823,8 @@ nfsd4_cld_remove(struct nfs4_client *clp) { int ret; struct cld_upcall *cup; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + /* FIXME: determine net from clp */ + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); struct cld_net *cn = nn->cld_net; /* Don't upcall if it's already removed */ @@ -982,7 +861,8 @@ nfsd4_cld_check(struct nfs4_client *clp) { int ret; struct cld_upcall *cup; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + /* FIXME: determine net from clp */ + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); struct cld_net *cn = nn->cld_net; /* Don't upcall if one was already stored during this grace pd */ @@ -1012,10 +892,11 @@ nfsd4_cld_check(struct nfs4_client *clp) } static void -nfsd4_cld_grace_done(struct nfsd_net *nn, time_t boot_time) +nfsd4_cld_grace_done(struct net *net, time_t boot_time) { int ret; struct cld_upcall *cup; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct cld_net *cn = nn->cld_net; cup = alloc_cld_upcall(cn); @@ -1045,261 +926,28 @@ static struct nfsd4_client_tracking_ops nfsd4_cld_tracking_ops = { .grace_done = nfsd4_cld_grace_done, }; -/* upcall via usermodehelper */ -static char cltrack_prog[PATH_MAX] = "/sbin/nfsdcltrack"; -module_param_string(cltrack_prog, cltrack_prog, sizeof(cltrack_prog), - S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(cltrack_prog, "Path to the nfsdcltrack upcall program"); - -static bool cltrack_legacy_disable; -module_param(cltrack_legacy_disable, bool, S_IRUGO|S_IWUSR); -MODULE_PARM_DESC(cltrack_legacy_disable, - "Disable legacy recoverydir conversion. Default: false"); - -#define LEGACY_TOPDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_TOPDIR=" -#define LEGACY_RECDIR_ENV_PREFIX "NFSDCLTRACK_LEGACY_RECDIR=" - -static char * -nfsd4_cltrack_legacy_topdir(void) -{ - int copied; - size_t len; - char *result; - - if (cltrack_legacy_disable) - return NULL; - - len = strlen(LEGACY_TOPDIR_ENV_PREFIX) + - strlen(nfs4_recoverydir()) + 1; - - result = kmalloc(len, GFP_KERNEL); - if (!result) - return result; - - copied = snprintf(result, len, LEGACY_TOPDIR_ENV_PREFIX "%s", - nfs4_recoverydir()); - if (copied >= len) { - /* just return nothing if output was truncated */ - kfree(result); - return NULL; - } - - return result; -} - -static char * -nfsd4_cltrack_legacy_recdir(const struct xdr_netobj *name) -{ - int copied; - size_t len; - char *result; - - if (cltrack_legacy_disable) - return NULL; - - /* +1 is for '/' between "topdir" and "recdir" */ - len = strlen(LEGACY_RECDIR_ENV_PREFIX) + - strlen(nfs4_recoverydir()) + 1 + HEXDIR_LEN; - - result = kmalloc(len, GFP_KERNEL); - if (!result) - return result; - - copied = snprintf(result, len, LEGACY_RECDIR_ENV_PREFIX "%s/", - nfs4_recoverydir()); - if (copied > (len - HEXDIR_LEN)) { - /* just return nothing if output will be truncated */ - kfree(result); - return NULL; - } - - copied = nfs4_make_rec_clidname(result + copied, name); - if (copied) { - kfree(result); - return NULL; - } - - return result; -} - -static int -nfsd4_umh_cltrack_upcall(char *cmd, char *arg, char *legacy) -{ - char *envp[2]; - char *argv[4]; - int ret; - - if (unlikely(!cltrack_prog[0])) { - dprintk("%s: cltrack_prog is disabled\n", __func__); - return -EACCES; - } - - dprintk("%s: cmd: %s\n", __func__, cmd); - dprintk("%s: arg: %s\n", __func__, arg ? arg : "(null)"); - dprintk("%s: legacy: %s\n", __func__, legacy ? legacy : "(null)"); - - envp[0] = legacy; - envp[1] = NULL; - - argv[0] = (char *)cltrack_prog; - argv[1] = cmd; - argv[2] = arg; - argv[3] = NULL; - - ret = call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); - /* - * Disable the upcall mechanism if we're getting an ENOENT or EACCES - * error. The admin can re-enable it on the fly by using sysfs - * once the problem has been fixed. - */ - if (ret == -ENOENT || ret == -EACCES) { - dprintk("NFSD: %s was not found or isn't executable (%d). " - "Setting cltrack_prog to blank string!", - cltrack_prog, ret); - cltrack_prog[0] = '\0'; - } - dprintk("%s: %s return value: %d\n", __func__, cltrack_prog, ret); - - return ret; -} - -static char * -bin_to_hex_dup(const unsigned char *src, int srclen) -{ - int i; - char *buf, *hex; - - /* +1 for terminating NULL */ - buf = kmalloc((srclen * 2) + 1, GFP_KERNEL); - if (!buf) - return buf; - - hex = buf; - for (i = 0; i < srclen; i++) { - sprintf(hex, "%2.2x", *src++); - hex += 2; - } - return buf; -} - -static int -nfsd4_umh_cltrack_init(struct net __attribute__((unused)) *net) -{ - return nfsd4_umh_cltrack_upcall("init", NULL, NULL); -} - -static void -nfsd4_umh_cltrack_create(struct nfs4_client *clp) -{ - char *hexid; - - hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); - if (!hexid) { - dprintk("%s: can't allocate memory for upcall!\n", __func__); - return; - } - nfsd4_umh_cltrack_upcall("create", hexid, NULL); - kfree(hexid); -} - -static void -nfsd4_umh_cltrack_remove(struct nfs4_client *clp) -{ - char *hexid; - - hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); - if (!hexid) { - dprintk("%s: can't allocate memory for upcall!\n", __func__); - return; - } - nfsd4_umh_cltrack_upcall("remove", hexid, NULL); - kfree(hexid); -} - -static int -nfsd4_umh_cltrack_check(struct nfs4_client *clp) -{ - int ret; - char *hexid, *legacy; - - hexid = bin_to_hex_dup(clp->cl_name.data, clp->cl_name.len); - if (!hexid) { - dprintk("%s: can't allocate memory for upcall!\n", __func__); - return -ENOMEM; - } - legacy = nfsd4_cltrack_legacy_recdir(&clp->cl_name); - ret = nfsd4_umh_cltrack_upcall("check", hexid, legacy); - kfree(legacy); - kfree(hexid); - return ret; -} - -static void -nfsd4_umh_cltrack_grace_done(struct nfsd_net __attribute__((unused)) *nn, - time_t boot_time) -{ - char *legacy; - char timestr[22]; /* FIXME: better way to determine max size? */ - - sprintf(timestr, "%ld", boot_time); - legacy = nfsd4_cltrack_legacy_topdir(); - nfsd4_umh_cltrack_upcall("gracedone", timestr, legacy); - kfree(legacy); -} - -static struct nfsd4_client_tracking_ops nfsd4_umh_tracking_ops = { - .init = nfsd4_umh_cltrack_init, - .exit = NULL, - .create = nfsd4_umh_cltrack_create, - .remove = nfsd4_umh_cltrack_remove, - .check = nfsd4_umh_cltrack_check, - .grace_done = nfsd4_umh_cltrack_grace_done, -}; - int nfsd4_client_tracking_init(struct net *net) { int status; struct path path; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - /* just run the init if it the method is already decided */ - if (nn->client_tracking_ops) - goto do_init; - - /* - * First, try a UMH upcall. It should succeed or fail quickly, so - * there's little harm in trying that first. - */ - nn->client_tracking_ops = &nfsd4_umh_tracking_ops; - status = nn->client_tracking_ops->init(net); - if (!status) - return status; - /* - * See if the recoverydir exists and is a directory. If it is, - * then use the legacy ops. - */ - nn->client_tracking_ops = &nfsd4_legacy_tracking_ops; - status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); - if (!status) { - status = S_ISDIR(path.dentry->d_inode->i_mode); - path_put(&path); - if (status) - goto do_init; + if (!client_tracking_ops) { + client_tracking_ops = &nfsd4_cld_tracking_ops; + status = kern_path(nfs4_recoverydir(), LOOKUP_FOLLOW, &path); + if (!status) { + if (S_ISDIR(path.dentry->d_inode->i_mode)) + client_tracking_ops = + &nfsd4_legacy_tracking_ops; + path_put(&path); + } } - /* Finally, try to use nfsdcld */ - nn->client_tracking_ops = &nfsd4_cld_tracking_ops; - printk(KERN_WARNING "NFSD: the nfsdcld client tracking upcall will be " - "removed in 3.10. Please transition to using " - "nfsdcltrack.\n"); -do_init: - status = nn->client_tracking_ops->init(net); + status = client_tracking_ops->init(net); if (status) { printk(KERN_WARNING "NFSD: Unable to initialize client " "recovery tracking! (%d)\n", status); - nn->client_tracking_ops = NULL; + client_tracking_ops = NULL; } return status; } @@ -1307,49 +955,40 @@ nfsd4_client_tracking_init(struct net *net) void nfsd4_client_tracking_exit(struct net *net) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - if (nn->client_tracking_ops) { - if (nn->client_tracking_ops->exit) - nn->client_tracking_ops->exit(net); - nn->client_tracking_ops = NULL; + if (client_tracking_ops) { + client_tracking_ops->exit(net); + client_tracking_ops = NULL; } } void nfsd4_client_record_create(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - if (nn->client_tracking_ops) - nn->client_tracking_ops->create(clp); + if (client_tracking_ops) + client_tracking_ops->create(clp); } void nfsd4_client_record_remove(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - if (nn->client_tracking_ops) - nn->client_tracking_ops->remove(clp); + if (client_tracking_ops) + client_tracking_ops->remove(clp); } int nfsd4_client_record_check(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - if (nn->client_tracking_ops) - return nn->client_tracking_ops->check(clp); + if (client_tracking_ops) + return client_tracking_ops->check(clp); return -EOPNOTSUPP; } void -nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time) +nfsd4_record_grace_done(struct net *net, time_t boot_time) { - if (nn->client_tracking_ops) - nn->client_tracking_ops->grace_done(nn, boot_time); + if (client_tracking_ops) + client_tracking_ops->grace_done(net, boot_time); } static int diff --git a/trunk/fs/nfsd/nfs4state.c b/trunk/fs/nfsd/nfs4state.c index ac8ed96c4199..d0237f872cc4 100644 --- a/trunk/fs/nfsd/nfs4state.c +++ b/trunk/fs/nfsd/nfs4state.c @@ -44,11 +44,16 @@ #include "xdr4.h" #include "vfs.h" #include "current_stateid.h" +#include "fault_inject.h" #include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_PROC +/* Globals */ +time_t nfsd4_lease = 90; /* default lease time */ +time_t nfsd4_grace = 90; + #define all_ones {{~0,~0},~0} static const stateid_t one_stateid = { .si_generation = ~0, @@ -171,6 +176,8 @@ static unsigned int ownerstr_hashval(u32 clientid, struct xdr_netobj *ownername) return ret & OWNER_HASH_MASK; } +static struct list_head ownerstr_hashtbl[OWNER_HASH_SIZE]; + /* hash table for nfs4_file */ #define FILE_HASH_BITS 8 #define FILE_HASH_SIZE (1 << FILE_HASH_BITS) @@ -185,7 +192,7 @@ static struct list_head file_hashtbl[FILE_HASH_SIZE]; static void __nfs4_file_get_access(struct nfs4_file *fp, int oflag) { - WARN_ON_ONCE(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); + BUG_ON(!(fp->fi_fds[oflag] || fp->fi_fds[O_RDWR])); atomic_inc(&fp->fi_access[oflag]); } @@ -244,7 +251,7 @@ static inline int get_new_stid(struct nfs4_stid *stid) * preallocations that can exist at a time, but the state lock * prevents anyone from using ours before we get here: */ - WARN_ON_ONCE(error); + BUG_ON(error); /* * It shouldn't be a problem to reuse an opaque stateid value. * I don't think it is for 4.1. But with 4.0 I worry that, for @@ -333,7 +340,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv fh_copy_shallow(&dp->dl_fh, ¤t_fh->fh_handle); dp->dl_time = 0; atomic_set(&dp->dl_count, 1); - nfsd4_init_callback(&dp->dl_recall); + INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); return dp; } @@ -383,6 +390,14 @@ unhash_delegation(struct nfs4_delegation *dp) * SETCLIENTID state */ +/* client_lock protects the client lru list and session hash table */ +static DEFINE_SPINLOCK(client_lock); + +/* Hash tables for nfs4_clientid state */ +#define CLIENT_HASH_BITS 4 +#define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) +#define CLIENT_HASH_MASK (CLIENT_HASH_SIZE - 1) + static unsigned int clientid_hashval(u32 id) { return id & CLIENT_HASH_MASK; @@ -393,6 +408,31 @@ static unsigned int clientstr_hashval(const char *name) return opaque_hashval(name, 8) & CLIENT_HASH_MASK; } +/* + * reclaim_str_hashtbl[] holds known client info from previous reset/reboot + * used in reboot/reset lease grace period processing + * + * conf_id_hashtbl[], and conf_str_hashtbl[] hold confirmed + * setclientid_confirmed info. + * + * unconf_str_hastbl[] and unconf_id_hashtbl[] hold unconfirmed + * setclientid info. + * + * client_lru holds client queue ordered by nfs4_client.cl_time + * for lease renewal. + * + * close_lru holds (open) stateowner queue ordered by nfs4_stateowner.so_time + * for last close replay. + */ +static struct list_head reclaim_str_hashtbl[CLIENT_HASH_SIZE]; +static int reclaim_str_hashtbl_size = 0; +static struct list_head conf_id_hashtbl[CLIENT_HASH_SIZE]; +static struct list_head conf_str_hashtbl[CLIENT_HASH_SIZE]; +static struct list_head unconf_str_hashtbl[CLIENT_HASH_SIZE]; +static struct list_head unconf_id_hashtbl[CLIENT_HASH_SIZE]; +static struct list_head client_lru; +static struct list_head close_lru; + /* * We store the NONE, READ, WRITE, and BOTH bits separately in the * st_{access,deny}_bmap field of the stateid, in order to track not @@ -486,8 +526,7 @@ static int nfs4_access_to_omode(u32 access) case NFS4_SHARE_ACCESS_BOTH: return O_RDWR; } - WARN_ON_ONCE(1); - return O_RDONLY; + BUG(); } /* release all access and file references for a given stateid */ @@ -613,6 +652,9 @@ static void release_openowner(struct nfs4_openowner *oo) nfs4_free_openowner(oo); } +#define SESSION_HASH_SIZE 512 +static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; + static inline int hash_sessionid(struct nfs4_sessionid *sessionid) { @@ -743,12 +785,9 @@ static struct nfsd4_session *__alloc_session(int slotsize, int numslots) return NULL; } -static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, - struct nfsd4_channel_attrs *req, - int numslots, int slotsize, - struct nfsd_net *nn) +static void init_forechannel_attrs(struct nfsd4_channel_attrs *new, struct nfsd4_channel_attrs *req, int numslots, int slotsize) { - u32 maxrpc = nn->nfsd_serv->sv_max_mesg; + u32 maxrpc = nfsd_serv->sv_max_mesg; new->maxreqs = numslots; new->maxresp_cached = min_t(u32, req->maxresp_cached, @@ -867,27 +906,21 @@ static void __free_session(struct nfsd4_session *ses) static void free_session(struct kref *kref) { struct nfsd4_session *ses; - struct nfsd_net *nn; + lockdep_assert_held(&client_lock); ses = container_of(kref, struct nfsd4_session, se_ref); - nn = net_generic(ses->se_client->net, nfsd_net_id); - - lockdep_assert_held(&nn->client_lock); nfsd4_del_conns(ses); __free_session(ses); } void nfsd4_put_session(struct nfsd4_session *ses) { - struct nfsd_net *nn = net_generic(ses->se_client->net, nfsd_net_id); - - spin_lock(&nn->client_lock); + spin_lock(&client_lock); nfsd4_put_session_locked(ses); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); } -static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, - struct nfsd_net *nn) +static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan) { struct nfsd4_session *new; int numslots, slotsize; @@ -908,14 +941,13 @@ static struct nfsd4_session *alloc_session(struct nfsd4_channel_attrs *fchan, nfsd4_put_drc_mem(slotsize, fchan->maxreqs); return NULL; } - init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize, nn); + init_forechannel_attrs(&new->se_fchannel, fchan, numslots, slotsize); return new; } -static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) +static struct nfsd4_session *init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, struct nfs4_client *clp, struct nfsd4_create_session *cses) { int idx; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); new->se_client = clp; gen_sessionid(new); @@ -925,15 +957,14 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru new->se_cb_seq_nr = 1; new->se_flags = cses->flags; new->se_cb_prog = cses->callback_prog; - new->se_cb_sec = cses->cb_sec; kref_init(&new->se_ref); idx = hash_sessionid(&new->se_sessionid); - spin_lock(&nn->client_lock); - list_add(&new->se_hash, &nn->sessionid_hashtbl[idx]); + spin_lock(&client_lock); + list_add(&new->se_hash, &sessionid_hashtbl[idx]); spin_lock(&clp->cl_lock); list_add(&new->se_perclnt, &clp->cl_sessions); spin_unlock(&clp->cl_lock); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); if (cses->flags & SESSION4_BACK_CHAN) { struct sockaddr *sa = svc_addr(rqstp); @@ -947,20 +978,20 @@ static void init_session(struct svc_rqst *rqstp, struct nfsd4_session *new, stru rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa); clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa); } + return new; } /* caller must hold client_lock */ static struct nfsd4_session * -find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net) +find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) { struct nfsd4_session *elem; int idx; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); dump_sessionid(__func__, sessionid); idx = hash_sessionid(sessionid); /* Search in the appropriate list */ - list_for_each_entry(elem, &nn->sessionid_hashtbl[idx], se_hash) { + list_for_each_entry(elem, &sessionid_hashtbl[idx], se_hash) { if (!memcmp(elem->se_sessionid.data, sessionid->data, NFS4_MAX_SESSIONID_LEN)) { return elem; @@ -985,8 +1016,6 @@ unhash_session(struct nfsd4_session *ses) static inline void renew_client_locked(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - if (is_client_expired(clp)) { WARN_ON(1); printk("%s: client (clientid %08x/%08x) already expired\n", @@ -999,18 +1028,16 @@ renew_client_locked(struct nfs4_client *clp) dprintk("renewing client (clientid %08x/%08x)\n", clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id); - list_move_tail(&clp->cl_lru, &nn->client_lru); + list_move_tail(&clp->cl_lru, &client_lru); clp->cl_time = get_seconds(); } static inline void renew_client(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - spin_lock(&nn->client_lock); + spin_lock(&client_lock); renew_client_locked(clp); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); } /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ @@ -1048,9 +1075,7 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) static inline void free_client(struct nfs4_client *clp) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - lockdep_assert_held(&nn->client_lock); + lockdep_assert_held(&client_lock); while (!list_empty(&clp->cl_sessions)) { struct nfsd4_session *ses; ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, @@ -1067,16 +1092,15 @@ void release_session_client(struct nfsd4_session *session) { struct nfs4_client *clp = session->se_client; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - if (!atomic_dec_and_lock(&clp->cl_refcount, &nn->client_lock)) + if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) return; if (is_client_expired(clp)) { free_client(clp); session->se_client = NULL; } else renew_client_locked(clp); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); } /* must be called under the client_lock */ @@ -1099,7 +1123,6 @@ destroy_client(struct nfs4_client *clp) struct nfs4_openowner *oo; struct nfs4_delegation *dp; struct list_head reaplist; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); @@ -1121,15 +1144,12 @@ destroy_client(struct nfs4_client *clp) if (clp->cl_cb_conn.cb_xprt) svc_xprt_put(clp->cl_cb_conn.cb_xprt); list_del(&clp->cl_idhash); - if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags)) - rb_erase(&clp->cl_namenode, &nn->conf_name_tree); - else - rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); - spin_lock(&nn->client_lock); + list_del(&clp->cl_strhash); + spin_lock(&client_lock); unhash_client_locked(clp); if (atomic_read(&clp->cl_refcount) == 0) free_client(clp); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); } static void expire_client(struct nfs4_client *clp) @@ -1167,17 +1187,6 @@ static int copy_cred(struct svc_cred *target, struct svc_cred *source) return 0; } -static long long -compare_blob(const struct xdr_netobj *o1, const struct xdr_netobj *o2) -{ - long long res; - - res = o1->len - o2->len; - if (res) - return res; - return (long long)memcmp(o1->data, o2->data, o1->len); -} - static int same_name(const char *n1, const char *n2) { return 0 == memcmp(n1, n2, HEXDIR_LEN); @@ -1238,9 +1247,10 @@ same_creds(struct svc_cred *cr1, struct svc_cred *cr2) return 0 == strcmp(cr1->cr_principal, cr2->cr_principal); } -static void gen_clid(struct nfs4_client *clp, struct nfsd_net *nn) +static void gen_clid(struct nfs4_client *clp) { static u32 current_clientid = 1; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); clp->cl_clientid.cl_boot = nn->boot_time; clp->cl_clientid.cl_id = current_clientid++; @@ -1273,14 +1283,12 @@ static struct nfs4_stid *find_stateid_by_type(struct nfs4_client *cl, stateid_t return NULL; } -static struct nfs4_client *create_client(struct xdr_netobj name, +static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, struct svc_rqst *rqstp, nfs4_verifier *verf) { struct nfs4_client *clp; struct sockaddr *sa = svc_addr(rqstp); int ret; - struct net *net = SVC_NET(rqstp); - struct nfsd_net *nn = net_generic(net, nfsd_net_id); clp = alloc_client(name); if (clp == NULL) @@ -1289,21 +1297,23 @@ static struct nfs4_client *create_client(struct xdr_netobj name, INIT_LIST_HEAD(&clp->cl_sessions); ret = copy_cred(&clp->cl_cred, &rqstp->rq_cred); if (ret) { - spin_lock(&nn->client_lock); + spin_lock(&client_lock); free_client(clp); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); return NULL; } idr_init(&clp->cl_stateids); + memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); atomic_set(&clp->cl_refcount, 0); clp->cl_cb_state = NFSD4_CB_UNKNOWN; INIT_LIST_HEAD(&clp->cl_idhash); + INIT_LIST_HEAD(&clp->cl_strhash); INIT_LIST_HEAD(&clp->cl_openowners); INIT_LIST_HEAD(&clp->cl_delegations); INIT_LIST_HEAD(&clp->cl_lru); INIT_LIST_HEAD(&clp->cl_callbacks); spin_lock_init(&clp->cl_lock); - nfsd4_init_callback(&clp->cl_cb_null); + INIT_WORK(&clp->cl_cb_null.cb_work, nfsd4_do_callback_rpc); clp->cl_time = get_seconds(); clear_bit(0, &clp->cl_cb_slot_busy); rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); @@ -1311,60 +1321,17 @@ static struct nfs4_client *create_client(struct xdr_netobj name, rpc_copy_addr((struct sockaddr *) &clp->cl_addr, sa); gen_confirm(clp); clp->cl_cb_session = NULL; - clp->net = net; return clp; } static void -add_clp_to_name_tree(struct nfs4_client *new_clp, struct rb_root *root) -{ - struct rb_node **new = &(root->rb_node), *parent = NULL; - struct nfs4_client *clp; - - while (*new) { - clp = rb_entry(*new, struct nfs4_client, cl_namenode); - parent = *new; - - if (compare_blob(&clp->cl_name, &new_clp->cl_name) > 0) - new = &((*new)->rb_left); - else - new = &((*new)->rb_right); - } - - rb_link_node(&new_clp->cl_namenode, parent, new); - rb_insert_color(&new_clp->cl_namenode, root); -} - -static struct nfs4_client * -find_clp_in_name_tree(struct xdr_netobj *name, struct rb_root *root) -{ - long long cmp; - struct rb_node *node = root->rb_node; - struct nfs4_client *clp; - - while (node) { - clp = rb_entry(node, struct nfs4_client, cl_namenode); - cmp = compare_blob(&clp->cl_name, name); - if (cmp > 0) - node = node->rb_left; - else if (cmp < 0) - node = node->rb_right; - else - return clp; - } - return NULL; -} - -static void -add_to_unconfirmed(struct nfs4_client *clp) +add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) { unsigned int idhashval; - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - clear_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); - add_clp_to_name_tree(clp, &nn->unconf_name_tree); + list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); idhashval = clientid_hashval(clp->cl_clientid.cl_id); - list_add(&clp->cl_idhash, &nn->unconf_id_hashtbl[idhashval]); + list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); renew_client(clp); } @@ -1372,23 +1339,22 @@ static void move_to_confirmed(struct nfs4_client *clp) { unsigned int idhashval = clientid_hashval(clp->cl_clientid.cl_id); - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); + unsigned int strhashval; dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); - list_move(&clp->cl_idhash, &nn->conf_id_hashtbl[idhashval]); - rb_erase(&clp->cl_namenode, &nn->unconf_name_tree); - add_clp_to_name_tree(clp, &nn->conf_name_tree); - set_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags); + list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); + strhashval = clientstr_hashval(clp->cl_recdir); + list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); renew_client(clp); } static struct nfs4_client * -find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) +find_confirmed_client(clientid_t *clid, bool sessions) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); - list_for_each_entry(clp, &nn->conf_id_hashtbl[idhashval], cl_idhash) { + list_for_each_entry(clp, &conf_id_hashtbl[idhashval], cl_idhash) { if (same_clid(&clp->cl_clientid, clid)) { if ((bool)clp->cl_minorversion != sessions) return NULL; @@ -1400,12 +1366,12 @@ find_confirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) } static struct nfs4_client * -find_unconfirmed_client(clientid_t *clid, bool sessions, struct nfsd_net *nn) +find_unconfirmed_client(clientid_t *clid, bool sessions) { struct nfs4_client *clp; unsigned int idhashval = clientid_hashval(clid->cl_id); - list_for_each_entry(clp, &nn->unconf_id_hashtbl[idhashval], cl_idhash) { + list_for_each_entry(clp, &unconf_id_hashtbl[idhashval], cl_idhash) { if (same_clid(&clp->cl_clientid, clid)) { if ((bool)clp->cl_minorversion != sessions) return NULL; @@ -1421,15 +1387,27 @@ static bool clp_used_exchangeid(struct nfs4_client *clp) } static struct nfs4_client * -find_confirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) +find_confirmed_client_by_str(const char *dname, unsigned int hashval) { - return find_clp_in_name_tree(name, &nn->conf_name_tree); + struct nfs4_client *clp; + + list_for_each_entry(clp, &conf_str_hashtbl[hashval], cl_strhash) { + if (same_name(clp->cl_recdir, dname)) + return clp; + } + return NULL; } static struct nfs4_client * -find_unconfirmed_client_by_name(struct xdr_netobj *name, struct nfsd_net *nn) +find_unconfirmed_client_by_str(const char *dname, unsigned int hashval) { - return find_clp_in_name_tree(name, &nn->unconf_name_tree); + struct nfs4_client *clp; + + list_for_each_entry(clp, &unconf_str_hashtbl[hashval], cl_strhash) { + if (same_name(clp->cl_recdir, dname)) + return clp; + } + return NULL; } static void @@ -1450,7 +1428,7 @@ gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se, struct svc_r else goto out_err; - conn->cb_addrlen = rpc_uaddr2sockaddr(clp->net, se->se_callback_addr_val, + conn->cb_addrlen = rpc_uaddr2sockaddr(&init_net, se->se_callback_addr_val, se->se_callback_addr_len, (struct sockaddr *)&conn->cb_addr, sizeof(conn->cb_addr)); @@ -1594,11 +1572,12 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, { struct nfs4_client *unconf, *conf, *new; __be32 status; + unsigned int strhashval; + char dname[HEXDIR_LEN]; char addr_str[INET6_ADDRSTRLEN]; nfs4_verifier verf = exid->verifier; struct sockaddr *sa = svc_addr(rqstp); bool update = exid->flags & EXCHGID4_FLAG_UPD_CONFIRMED_REC_A; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); rpc_ntop(sa, addr_str, sizeof(addr_str)); dprintk("%s rqstp=%p exid=%p clname.len=%u clname.data=%p " @@ -1613,16 +1592,24 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, switch (exid->spa_how) { case SP4_NONE: break; - default: /* checked by xdr code */ - WARN_ON_ONCE(1); case SP4_SSV: + return nfserr_serverfault; + default: + BUG(); /* checked by xdr code */ case SP4_MACH_CRED: return nfserr_serverfault; /* no excuse :-/ */ } + status = nfs4_make_rec_clidname(dname, &exid->clname); + + if (status) + return status; + + strhashval = clientstr_hashval(dname); + /* Cases below refer to rfc 5661 section 18.35.4: */ nfs4_lock_state(); - conf = find_confirmed_client_by_name(&exid->clname, nn); + conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { bool creds_match = same_creds(&conf->cl_cred, &rqstp->rq_cred); bool verfs_match = same_verf(&verf, &conf->cl_verifier); @@ -1667,21 +1654,21 @@ nfsd4_exchange_id(struct svc_rqst *rqstp, goto out; } - unconf = find_unconfirmed_client_by_name(&exid->clname, nn); + unconf = find_unconfirmed_client_by_str(dname, strhashval); if (unconf) /* case 4, possible retry or client restart */ expire_client(unconf); /* case 1 (normal case) */ out_new: - new = create_client(exid->clname, rqstp, &verf); + new = create_client(exid->clname, dname, rqstp, &verf); if (new == NULL) { status = nfserr_jukebox; goto out; } new->cl_minorversion = 1; - gen_clid(new, nn); - add_to_unconfirmed(new); + gen_clid(new); + add_to_unconfirmed(new, strhashval); out_copy: exid->clientid.cl_boot = new->cl_clientid.cl_boot; exid->clientid.cl_id = new->cl_clientid.cl_id; @@ -1774,13 +1761,12 @@ nfsd4_create_session(struct svc_rqst *rqstp, struct nfsd4_conn *conn; struct nfsd4_clid_slot *cs_slot = NULL; __be32 status = 0; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (cr_ses->flags & ~SESSION4_FLAG_MASK_A) return nfserr_inval; if (check_forechannel_attrs(cr_ses->fore_channel)) return nfserr_toosmall; - new = alloc_session(&cr_ses->fore_channel, nn); + new = alloc_session(&cr_ses->fore_channel); if (!new) return nfserr_jukebox; status = nfserr_jukebox; @@ -1789,8 +1775,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out_free_session; nfs4_lock_state(); - unconf = find_unconfirmed_client(&cr_ses->clientid, true, nn); - conf = find_confirmed_client(&cr_ses->clientid, true, nn); + unconf = find_unconfirmed_client(&cr_ses->clientid, true); + conf = find_confirmed_client(&cr_ses->clientid, true); if (conf) { cs_slot = &conf->cl_cs_slot; @@ -1803,6 +1789,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out_free_conn; } } else if (unconf) { + unsigned int hash; struct nfs4_client *old; if (!same_creds(&unconf->cl_cred, &rqstp->rq_cred) || !rpc_cmp_addr(sa, (struct sockaddr *) &unconf->cl_addr)) { @@ -1816,7 +1803,8 @@ nfsd4_create_session(struct svc_rqst *rqstp, status = nfserr_seq_misordered; goto out_free_conn; } - old = find_confirmed_client_by_name(&unconf->cl_name, nn); + hash = clientstr_hashval(unconf->cl_recdir); + old = find_confirmed_client_by_str(unconf->cl_recdir, hash); if (old) expire_client(old); move_to_confirmed(unconf); @@ -1855,6 +1843,14 @@ nfsd4_create_session(struct svc_rqst *rqstp, goto out; } +static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) +{ + struct nfsd4_compoundres *resp = rqstp->rq_resp; + struct nfsd4_compoundargs *argp = rqstp->rq_argp; + + return argp->opcnt == resp->opcnt; +} + static __be32 nfsd4_map_bcts_dir(u32 *dir) { switch (*dir) { @@ -1869,40 +1865,24 @@ static __be32 nfsd4_map_bcts_dir(u32 *dir) return nfserr_inval; } -__be32 nfsd4_backchannel_ctl(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_backchannel_ctl *bc) -{ - struct nfsd4_session *session = cstate->session; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - - spin_lock(&nn->client_lock); - session->se_cb_prog = bc->bc_cb_program; - session->se_cb_sec = bc->bc_cb_sec; - spin_unlock(&nn->client_lock); - - nfsd4_probe_callback(session->se_client); - - return nfs_ok; -} - __be32 nfsd4_bind_conn_to_session(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_bind_conn_to_session *bcts) { __be32 status; struct nfsd4_conn *conn; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (!nfsd4_last_compound_op(rqstp)) return nfserr_not_only_op; - spin_lock(&nn->client_lock); - cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid, SVC_NET(rqstp)); + spin_lock(&client_lock); + cstate->session = find_in_sessionid_hashtbl(&bcts->sessionid); /* Sorta weird: we only need the refcnt'ing because new_conn acquires * client_lock iself: */ if (cstate->session) { nfsd4_get_session(cstate->session); atomic_inc(&cstate->session->se_client->cl_refcount); } - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); if (!cstate->session) return nfserr_badsession; @@ -1930,7 +1910,6 @@ nfsd4_destroy_session(struct svc_rqst *r, { struct nfsd4_session *ses; __be32 status = nfserr_badsession; - struct nfsd_net *nn = net_generic(SVC_NET(r), nfsd_net_id); /* Notes: * - The confirmed nfs4_client->cl_sessionid holds destroyed sessinid @@ -1944,24 +1923,24 @@ nfsd4_destroy_session(struct svc_rqst *r, return nfserr_not_only_op; } dump_sessionid(__func__, &sessionid->sessionid); - spin_lock(&nn->client_lock); - ses = find_in_sessionid_hashtbl(&sessionid->sessionid, SVC_NET(r)); + spin_lock(&client_lock); + ses = find_in_sessionid_hashtbl(&sessionid->sessionid); if (!ses) { - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); goto out; } unhash_session(ses); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); nfs4_lock_state(); nfsd4_probe_callback_sync(ses->se_client); nfs4_unlock_state(); - spin_lock(&nn->client_lock); + spin_lock(&client_lock); nfsd4_del_conns(ses); nfsd4_put_session_locked(ses); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); status = nfs_ok; out: dprintk("%s returns %d\n", __func__, ntohl(status)); @@ -2027,7 +2006,6 @@ nfsd4_sequence(struct svc_rqst *rqstp, struct nfsd4_slot *slot; struct nfsd4_conn *conn; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if (resp->opcnt != 1) return nfserr_sequence_pos; @@ -2040,9 +2018,9 @@ nfsd4_sequence(struct svc_rqst *rqstp, if (!conn) return nfserr_jukebox; - spin_lock(&nn->client_lock); + spin_lock(&client_lock); status = nfserr_badsession; - session = find_in_sessionid_hashtbl(&seq->sessionid, SVC_NET(rqstp)); + session = find_in_sessionid_hashtbl(&seq->sessionid); if (!session) goto out; @@ -2116,7 +2094,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, } } kfree(conn); - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); dprintk("%s: return %d\n", __func__, ntohl(status)); return status; } @@ -2126,11 +2104,10 @@ nfsd4_destroy_clientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *csta { struct nfs4_client *conf, *unconf, *clp; __be32 status = 0; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); nfs4_lock_state(); - unconf = find_unconfirmed_client(&dc->clientid, true, nn); - conf = find_confirmed_client(&dc->clientid, true, nn); + unconf = find_unconfirmed_client(&dc->clientid, true); + conf = find_confirmed_client(&dc->clientid, true); if (conf) { clp = conf; @@ -2204,13 +2181,20 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, { struct xdr_netobj clname = setclid->se_name; nfs4_verifier clverifier = setclid->se_verf; + unsigned int strhashval; struct nfs4_client *conf, *unconf, *new; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + char dname[HEXDIR_LEN]; + + status = nfs4_make_rec_clidname(dname, &clname); + if (status) + return status; + + strhashval = clientstr_hashval(dname); /* Cases below refer to rfc 3530 section 14.2.33: */ nfs4_lock_state(); - conf = find_confirmed_client_by_name(&clname, nn); + conf = find_confirmed_client_by_str(dname, strhashval); if (conf) { /* case 0: */ status = nfserr_clid_inuse; @@ -2225,21 +2209,21 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } } - unconf = find_unconfirmed_client_by_name(&clname, nn); + unconf = find_unconfirmed_client_by_str(dname, strhashval); if (unconf) expire_client(unconf); status = nfserr_jukebox; - new = create_client(clname, rqstp, &clverifier); + new = create_client(clname, dname, rqstp, &clverifier); if (new == NULL) goto out; if (conf && same_verf(&conf->cl_verifier, &clverifier)) /* case 1: probable callback update */ copy_clid(new, conf); else /* case 4 (new client) or cases 2, 3 (client reboot): */ - gen_clid(new, nn); + gen_clid(new); new->cl_minorversion = 0; gen_callback(new, setclid, rqstp); - add_to_unconfirmed(new); + add_to_unconfirmed(new, strhashval); setclid->se_clientid.cl_boot = new->cl_clientid.cl_boot; setclid->se_clientid.cl_id = new->cl_clientid.cl_id; memcpy(setclid->se_confirm.data, new->cl_confirm.data, sizeof(setclid->se_confirm.data)); @@ -2259,14 +2243,14 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfs4_verifier confirm = setclientid_confirm->sc_confirm; clientid_t * clid = &setclientid_confirm->sc_clientid; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (STALE_CLIENTID(clid, nn)) return nfserr_stale_clientid; nfs4_lock_state(); - conf = find_confirmed_client(clid, false, nn); - unconf = find_unconfirmed_client(clid, false, nn); + conf = find_confirmed_client(clid, false); + unconf = find_unconfirmed_client(clid, false); /* * We try hard to give out unique clientid's, so if we get an * attempt to confirm the same clientid with a different cred, @@ -2292,7 +2276,9 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, nfsd4_probe_callback(conf); expire_client(unconf); } else { /* case 3: normal case; new or rebooted client */ - conf = find_confirmed_client_by_name(&unconf->cl_name, nn); + unsigned int hash = clientstr_hashval(unconf->cl_recdir); + + conf = find_confirmed_client_by_str(unconf->cl_recdir, hash); if (conf) expire_client(conf); move_to_confirmed(unconf); @@ -2354,7 +2340,7 @@ nfsd4_init_slabs(void) if (openowner_slab == NULL) goto out_nomem; lockowner_slab = kmem_cache_create("nfsd4_lockowners", - sizeof(struct nfs4_lockowner), 0, 0, NULL); + sizeof(struct nfs4_openowner), 0, 0, NULL); if (lockowner_slab == NULL) goto out_nomem; file_slab = kmem_cache_create("nfsd4_files", @@ -2418,9 +2404,7 @@ static inline void *alloc_stateowner(struct kmem_cache *slab, struct xdr_netobj static void hash_openowner(struct nfs4_openowner *oo, struct nfs4_client *clp, unsigned int strhashval) { - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - - list_add(&oo->oo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); + list_add(&oo->oo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); list_add(&oo->oo_perclient, &clp->cl_openowners); } @@ -2460,13 +2444,11 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, } static void -move_to_close_lru(struct nfs4_openowner *oo, struct net *net) +move_to_close_lru(struct nfs4_openowner *oo) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - dprintk("NFSD: move_to_close_lru nfs4_openowner %p\n", oo); - list_move_tail(&oo->oo_close_lru, &nn->close_lru); + list_move_tail(&oo->oo_close_lru, &close_lru); oo->oo_time = get_seconds(); } @@ -2480,14 +2462,13 @@ same_owner_str(struct nfs4_stateowner *sop, struct xdr_netobj *owner, } static struct nfs4_openowner * -find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, - bool sessions, struct nfsd_net *nn) +find_openstateowner_str(unsigned int hashval, struct nfsd4_open *open, bool sessions) { struct nfs4_stateowner *so; struct nfs4_openowner *oo; struct nfs4_client *clp; - list_for_each_entry(so, &nn->ownerstr_hashtbl[hashval], so_strhash) { + list_for_each_entry(so, &ownerstr_hashtbl[hashval], so_strhash) { if (!so->so_is_open_owner) continue; if (same_owner_str(so, &open->op_owner, &open->op_clientid)) { @@ -2574,14 +2555,9 @@ static void nfsd_break_deleg_cb(struct file_lock *fl) struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner; struct nfs4_delegation *dp; - if (!fp) { - WARN(1, "(%p)->fl_owner NULL\n", fl); - return; - } - if (fp->fi_had_conflict) { - WARN(1, "duplicate break on %p\n", fp); - return; - } + BUG_ON(!fp); + /* We assume break_lease is only called once per lease: */ + BUG_ON(fp->fi_had_conflict); /* * We don't want the locks code to timeout the lease for us; * we'll remove it ourself if a delegation isn't returned @@ -2623,13 +2599,14 @@ static __be32 nfsd4_check_seqid(struct nfsd4_compound_state *cstate, struct nfs4 __be32 nfsd4_process_open1(struct nfsd4_compound_state *cstate, - struct nfsd4_open *open, struct nfsd_net *nn) + struct nfsd4_open *open) { clientid_t *clientid = &open->op_clientid; struct nfs4_client *clp = NULL; unsigned int strhashval; struct nfs4_openowner *oo = NULL; __be32 status; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (STALE_CLIENTID(&open->op_clientid, nn)) return nfserr_stale_clientid; @@ -2642,11 +2619,10 @@ nfsd4_process_open1(struct nfsd4_compound_state *cstate, return nfserr_jukebox; strhashval = ownerstr_hashval(clientid->cl_id, &open->op_owner); - oo = find_openstateowner_str(strhashval, open, cstate->minorversion, nn); + oo = find_openstateowner_str(strhashval, open, cstate->minorversion); open->op_openowner = oo; if (!oo) { - clp = find_confirmed_client(clientid, cstate->minorversion, - nn); + clp = find_confirmed_client(clientid, cstate->minorversion); if (clp == NULL) return nfserr_expired; goto new_owner; @@ -2915,7 +2891,7 @@ static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) open->op_why_no_deleg = WND4_CANCELLED; break; case NFS4_SHARE_WANT_NO_DELEG: - WARN_ON_ONCE(1); + BUG(); /* not supposed to get here */ } } } @@ -2983,7 +2959,6 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh, } return; out_free: - unhash_stid(&dp->dl_stid); nfs4_put_delegation(dp); out_no_deleg: flag = NFS4_OPEN_DELEGATE_NONE; @@ -3129,32 +3104,27 @@ void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status) free_generic_stateid(open->op_stp); } -static __be32 lookup_clientid(clientid_t *clid, bool session, struct nfsd_net *nn, struct nfs4_client **clp) -{ - struct nfs4_client *found; - - if (STALE_CLIENTID(clid, nn)) - return nfserr_stale_clientid; - found = find_confirmed_client(clid, session, nn); - if (clp) - *clp = found; - return found ? nfs_ok : nfserr_expired; -} - __be32 nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, clientid_t *clid) { struct nfs4_client *clp; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); nfs4_lock_state(); dprintk("process_renew(%08x/%08x): starting\n", clid->cl_boot, clid->cl_id); - status = lookup_clientid(clid, cstate->minorversion, nn, &clp); - if (status) + status = nfserr_stale_clientid; + if (STALE_CLIENTID(clid, nn)) goto out; + clp = find_confirmed_client(clid, cstate->minorversion); + status = nfserr_expired; + if (clp == NULL) { + /* We assume the client took too long to RENEW. */ + dprintk("nfsd4_renew: clientid not found!\n"); + goto out; + } status = nfserr_cb_path_down; if (!list_empty(&clp->cl_delegations) && clp->cl_cb_state != NFSD4_CB_UP) @@ -3166,42 +3136,44 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, } static void -nfsd4_end_grace(struct nfsd_net *nn) +nfsd4_end_grace(struct net *net) { + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + /* do nothing if grace period already ended */ if (nn->grace_ended) return; dprintk("NFSD: end of grace period\n"); nn->grace_ended = true; - nfsd4_record_grace_done(nn, nn->boot_time); + nfsd4_record_grace_done(net, nn->boot_time); locks_end_grace(&nn->nfsd4_manager); /* * Now that every NFSv4 client has had the chance to recover and * to see the (possibly new, possibly shorter) lease time, we * can safely set the next grace time to the current lease time: */ - nn->nfsd4_grace = nn->nfsd4_lease; + nfsd4_grace = nfsd4_lease; } static time_t -nfs4_laundromat(struct nfsd_net *nn) +nfs4_laundromat(void) { struct nfs4_client *clp; struct nfs4_openowner *oo; struct nfs4_delegation *dp; struct list_head *pos, *next, reaplist; - time_t cutoff = get_seconds() - nn->nfsd4_lease; - time_t t, clientid_val = nn->nfsd4_lease; - time_t u, test_val = nn->nfsd4_lease; + time_t cutoff = get_seconds() - nfsd4_lease; + time_t t, clientid_val = nfsd4_lease; + time_t u, test_val = nfsd4_lease; nfs4_lock_state(); dprintk("NFSD: laundromat service - starting\n"); - nfsd4_end_grace(nn); + nfsd4_end_grace(&init_net); INIT_LIST_HEAD(&reaplist); - spin_lock(&nn->client_lock); - list_for_each_safe(pos, next, &nn->client_lru) { + spin_lock(&client_lock); + list_for_each_safe(pos, next, &client_lru) { clp = list_entry(pos, struct nfs4_client, cl_lru); if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { t = clp->cl_time - cutoff; @@ -3217,7 +3189,7 @@ nfs4_laundromat(struct nfsd_net *nn) unhash_client_locked(clp); list_add(&clp->cl_lru, &reaplist); } - spin_unlock(&nn->client_lock); + spin_unlock(&client_lock); list_for_each_safe(pos, next, &reaplist) { clp = list_entry(pos, struct nfs4_client, cl_lru); dprintk("NFSD: purging unused client (clientid %08x)\n", @@ -3227,8 +3199,6 @@ nfs4_laundromat(struct nfsd_net *nn) spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); - if (net_generic(dp->dl_stid.sc_client->net, nfsd_net_id) != nn) - continue; if (time_after((unsigned long)dp->dl_time, (unsigned long)cutoff)) { u = dp->dl_time - cutoff; if (test_val > u) @@ -3242,8 +3212,8 @@ nfs4_laundromat(struct nfsd_net *nn) dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); unhash_delegation(dp); } - test_val = nn->nfsd4_lease; - list_for_each_safe(pos, next, &nn->close_lru) { + test_val = nfsd4_lease; + list_for_each_safe(pos, next, &close_lru) { oo = container_of(pos, struct nfs4_openowner, oo_close_lru); if (time_after((unsigned long)oo->oo_time, (unsigned long)cutoff)) { u = oo->oo_time - cutoff; @@ -3261,19 +3231,16 @@ nfs4_laundromat(struct nfsd_net *nn) static struct workqueue_struct *laundry_wq; static void laundromat_main(struct work_struct *); +static DECLARE_DELAYED_WORK(laundromat_work, laundromat_main); static void -laundromat_main(struct work_struct *laundry) +laundromat_main(struct work_struct *not_used) { time_t t; - struct delayed_work *dwork = container_of(laundry, struct delayed_work, - work); - struct nfsd_net *nn = container_of(dwork, struct nfsd_net, - laundromat_work); - t = nfs4_laundromat(nn); + t = nfs4_laundromat(); dprintk("NFSD: laundromat_main - sleeping for %ld seconds\n", t); - queue_delayed_work(laundry_wq, &nn->laundromat_work, t*HZ); + queue_delayed_work(laundry_wq, &laundromat_work, t*HZ); } static inline __be32 nfs4_check_fh(struct svc_fh *fhp, struct nfs4_ol_stateid *stp) @@ -3418,17 +3385,16 @@ static __be32 nfsd4_validate_stateid(struct nfs4_client *cl, stateid_t *stateid) return nfs_ok; } -static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, - struct nfs4_stid **s, bool sessions, - struct nfsd_net *nn) +static __be32 nfsd4_lookup_stateid(stateid_t *stateid, unsigned char typemask, struct nfs4_stid **s, bool sessions) { struct nfs4_client *cl; + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return nfserr_bad_stateid; if (STALE_STATEID(stateid, nn)) return nfserr_stale_stateid; - cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions, nn); + cl = find_confirmed_client(&stateid->si_opaque.so_clid, sessions); if (!cl) return nfserr_expired; *s = find_stateid_by_type(cl, stateid, typemask); @@ -3450,7 +3416,6 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, struct nfs4_delegation *dp = NULL; struct svc_fh *current_fh = &cstate->current_fh; struct inode *ino = current_fh->fh_dentry->d_inode; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); __be32 status; if (filpp) @@ -3462,8 +3427,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, if (ZERO_STATEID(stateid) || ONE_STATEID(stateid)) return check_special_stateids(net, current_fh, stateid, flags); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, - &s, cstate->minorversion, nn); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID|NFS4_OPEN_STID|NFS4_LOCK_STID, &s, cstate->minorversion); if (status) return status; status = check_stateid_generation(stateid, &s->sc_stateid, nfsd4_has_session(cstate)); @@ -3477,11 +3441,7 @@ nfs4_preprocess_stateid_op(struct net *net, struct nfsd4_compound_state *cstate, goto out; if (filpp) { *filpp = dp->dl_file->fi_deleg_file; - if (!*filpp) { - WARN_ON_ONCE(1); - status = nfserr_serverfault; - goto out; - } + BUG_ON(!*filpp); } break; case NFS4_OPEN_STID: @@ -3608,8 +3568,7 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_ static __be32 nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, char typemask, - struct nfs4_ol_stateid **stpp, - struct nfsd_net *nn) + struct nfs4_ol_stateid **stpp) { __be32 status; struct nfs4_stid *s; @@ -3618,8 +3577,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, seqid, STATEID_VAL(stateid)); *stpp = NULL; - status = nfsd4_lookup_stateid(stateid, typemask, &s, - cstate->minorversion, nn); + status = nfsd4_lookup_stateid(stateid, typemask, &s, cstate->minorversion); if (status) return status; *stpp = openlockstateid(s); @@ -3628,14 +3586,13 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, return nfs4_seqid_op_checks(cstate, stateid, seqid, *stpp); } -static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, - stateid_t *stateid, struct nfs4_ol_stateid **stpp, struct nfsd_net *nn) +static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, stateid_t *stateid, struct nfs4_ol_stateid **stpp) { __be32 status; struct nfs4_openowner *oo; status = nfs4_preprocess_seqid_op(cstate, seqid, stateid, - NFS4_OPEN_STID, stpp, nn); + NFS4_OPEN_STID, stpp); if (status) return status; oo = openowner((*stpp)->st_stateowner); @@ -3651,7 +3608,6 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct nfs4_openowner *oo; struct nfs4_ol_stateid *stp; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("NFSD: nfsd4_open_confirm on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3665,7 +3621,7 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, oc->oc_seqid, &oc->oc_req_stateid, - NFS4_OPEN_STID, &stp, nn); + NFS4_OPEN_STID, &stp); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3708,7 +3664,7 @@ static inline void nfs4_stateid_downgrade(struct nfs4_ol_stateid *stp, u32 to_ac case NFS4_SHARE_ACCESS_BOTH: break; default: - WARN_ON_ONCE(1); + BUG(); } } @@ -3729,7 +3685,6 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, { __be32 status; struct nfs4_ol_stateid *stp; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); dprintk("NFSD: nfsd4_open_downgrade on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3742,7 +3697,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp, nfs4_lock_state(); status = nfs4_preprocess_confirmed_seqid_op(cstate, od->od_seqid, - &od->od_stateid, &stp, nn); + &od->od_stateid, &stp); if (status) goto out; status = nfserr_inval; @@ -3805,8 +3760,6 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, __be32 status; struct nfs4_openowner *oo; struct nfs4_ol_stateid *stp; - struct net *net = SVC_NET(rqstp); - struct nfsd_net *nn = net_generic(net, nfsd_net_id); dprintk("NFSD: nfsd4_close on file %.*s\n", (int)cstate->current_fh.fh_dentry->d_name.len, @@ -3816,7 +3769,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, close->cl_seqid, &close->cl_stateid, NFS4_OPEN_STID|NFS4_CLOSED_STID, - &stp, nn); + &stp); if (status) goto out; oo = openowner(stp->st_stateowner); @@ -3838,7 +3791,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, * little while to handle CLOSE replay. */ if (list_empty(&oo->oo_owner.so_stateids)) - move_to_close_lru(oo, SVC_NET(rqstp)); + move_to_close_lru(oo); } } out: @@ -3854,15 +3807,15 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfs4_delegation *dp; stateid_t *stateid = &dr->dr_stateid; struct nfs4_stid *s; + struct inode *inode; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) return status; + inode = cstate->current_fh.fh_dentry->d_inode; nfs4_lock_state(); - status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, - cstate->minorversion, nn); + status = nfsd4_lookup_stateid(stateid, NFS4_DELEG_STID, &s, cstate->minorversion); if (status) goto out; dp = delegstateid(s); @@ -3880,6 +3833,8 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, #define LOFF_OVERFLOW(start, len) ((u64)(len) > ~(u64)(start)) +#define LOCKOWNER_INO_HASH_BITS 8 +#define LOCKOWNER_INO_HASH_SIZE (1 << LOCKOWNER_INO_HASH_BITS) #define LOCKOWNER_INO_HASH_MASK (LOCKOWNER_INO_HASH_SIZE - 1) static inline u64 @@ -3897,7 +3852,7 @@ last_byte_offset(u64 start, u64 len) { u64 end; - WARN_ON_ONCE(!len); + BUG_ON(!len); end = start + len; return end > start ? end - 1: NFS4_MAX_UINT64; } @@ -3909,6 +3864,8 @@ static unsigned int lockowner_ino_hashval(struct inode *inode, u32 cl_id, struct & LOCKOWNER_INO_HASH_MASK; } +static struct list_head lockowner_ino_hashtbl[LOCKOWNER_INO_HASH_SIZE]; + /* * TODO: Linux file offsets are _signed_ 64-bit quantities, which means that * we can't properly handle lock requests that go beyond the (2^63 - 1)-th @@ -3974,12 +3931,12 @@ static bool same_lockowner_ino(struct nfs4_lockowner *lo, struct inode *inode, c static struct nfs4_lockowner * find_lockowner_str(struct inode *inode, clientid_t *clid, - struct xdr_netobj *owner, struct nfsd_net *nn) + struct xdr_netobj *owner) { unsigned int hashval = lockowner_ino_hashval(inode, clid->cl_id, owner); struct nfs4_lockowner *lo; - list_for_each_entry(lo, &nn->lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { + list_for_each_entry(lo, &lockowner_ino_hashtbl[hashval], lo_owner_ino_hash) { if (same_lockowner_ino(lo, inode, clid, owner)) return lo; } @@ -3991,10 +3948,9 @@ static void hash_lockowner(struct nfs4_lockowner *lo, unsigned int strhashval, s struct inode *inode = open_stp->st_file->fi_inode; unsigned int inohash = lockowner_ino_hashval(inode, clp->cl_clientid.cl_id, &lo->lo_owner.so_owner); - struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); - list_add(&lo->lo_owner.so_strhash, &nn->ownerstr_hashtbl[strhashval]); - list_add(&lo->lo_owner_ino_hash, &nn->lockowner_ino_hashtbl[inohash]); + list_add(&lo->lo_owner.so_strhash, &ownerstr_hashtbl[strhashval]); + list_add(&lo->lo_owner_ino_hash, &lockowner_ino_hashtbl[inohash]); list_add(&lo->lo_perstateid, &open_stp->st_lockowners); } @@ -4068,10 +4024,8 @@ static __be32 lookup_or_create_lock_state(struct nfsd4_compound_state *cstate, s struct nfs4_client *cl = oo->oo_owner.so_client; struct nfs4_lockowner *lo; unsigned int strhashval; - struct nfsd_net *nn = net_generic(cl->net, nfsd_net_id); - lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, - &lock->v.new.owner, nn); + lo = find_lockowner_str(fi->fi_inode, &cl->cl_clientid, &lock->v.new.owner); if (lo) { if (!cstate->minorversion) return nfserr_bad_seqid; @@ -4111,8 +4065,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, bool new_state = false; int lkflg; int err; - struct net *net = SVC_NET(rqstp); - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); dprintk("NFSD: nfsd4_lock: start=%Ld length=%Ld\n", (long long) lock->lk_offset, @@ -4146,7 +4099,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_confirmed_seqid_op(cstate, lock->lk_new_open_seqid, &lock->lk_new_open_stateid, - &open_stp, nn); + &open_stp); if (status) goto out; open_sop = openowner(open_stp->st_stateowner); @@ -4160,7 +4113,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, status = nfs4_preprocess_seqid_op(cstate, lock->lk_old_lock_seqid, &lock->lk_old_lock_stateid, - NFS4_LOCK_STID, &lock_stp, nn); + NFS4_LOCK_STID, &lock_stp); if (status) goto out; lock_sop = lockowner(lock_stp->st_stateowner); @@ -4171,10 +4124,10 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; status = nfserr_grace; - if (locks_in_grace(net) && !lock->lk_reclaim) + if (locks_in_grace(SVC_NET(rqstp)) && !lock->lk_reclaim) goto out; status = nfserr_no_grace; - if (!locks_in_grace(net) && lock->lk_reclaim) + if (!locks_in_grace(SVC_NET(rqstp)) && lock->lk_reclaim) goto out; file_lock = locks_alloc_lock(); @@ -4285,7 +4238,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock *file_lock = NULL; struct nfs4_lockowner *lo; __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); if (locks_in_grace(SVC_NET(rqstp))) return nfserr_grace; @@ -4295,11 +4248,9 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); - if (!nfsd4_has_session(cstate)) { - status = lookup_clientid(&lockt->lt_clientid, false, nn, NULL); - if (status) - goto out; - } + status = nfserr_stale_clientid; + if (!nfsd4_has_session(cstate) && STALE_CLIENTID(&lockt->lt_clientid, nn)) + goto out; if ((status = fh_verify(rqstp, &cstate->current_fh, S_IFREG, 0))) goto out; @@ -4327,7 +4278,7 @@ nfsd4_lockt(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, goto out; } - lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner, nn); + lo = find_lockowner_str(inode, &lockt->lt_clientid, &lockt->lt_owner); if (lo) file_lock->fl_owner = (fl_owner_t)lo; file_lock->fl_pid = current->tgid; @@ -4362,8 +4313,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct file_lock *file_lock = NULL; __be32 status; int err; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); - + dprintk("NFSD: nfsd4_locku: start=%Ld length=%Ld\n", (long long) locku->lu_offset, (long long) locku->lu_length); @@ -4374,8 +4324,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, nfs4_lock_state(); status = nfs4_preprocess_seqid_op(cstate, locku->lu_seqid, - &locku->lu_stateid, NFS4_LOCK_STID, - &stp, nn); + &locku->lu_stateid, NFS4_LOCK_STID, &stp); if (status) goto out; filp = find_any_file(stp->st_file); @@ -4465,21 +4414,23 @@ nfsd4_release_lockowner(struct svc_rqst *rqstp, struct list_head matches; unsigned int hashval = ownerstr_hashval(clid->cl_id, owner); __be32 status; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); + struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); dprintk("nfsd4_release_lockowner clientid: (%08x/%08x):\n", clid->cl_boot, clid->cl_id); - nfs4_lock_state(); + /* XXX check for lease expiration */ - status = lookup_clientid(clid, cstate->minorversion, nn, NULL); - if (status) - goto out; + status = nfserr_stale_clientid; + if (STALE_CLIENTID(clid, nn)) + return status; + + nfs4_lock_state(); status = nfserr_locks_held; INIT_LIST_HEAD(&matches); - list_for_each_entry(sop, &nn->ownerstr_hashtbl[hashval], so_strhash) { + list_for_each_entry(sop, &ownerstr_hashtbl[hashval], so_strhash) { if (sop->so_is_open_owner) continue; if (!same_owner_str(sop, owner, clid)) @@ -4515,74 +4466,73 @@ alloc_reclaim(void) return kmalloc(sizeof(struct nfs4_client_reclaim), GFP_KERNEL); } -bool -nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn) +int +nfs4_has_reclaimed_state(const char *name, bool use_exchange_id) { - struct nfs4_client_reclaim *crp; + unsigned int strhashval = clientstr_hashval(name); + struct nfs4_client *clp; - crp = nfsd4_find_reclaim_client(name, nn); - return (crp && crp->cr_clp); + clp = find_confirmed_client_by_str(name, strhashval); + if (!clp) + return 0; + return test_bit(NFSD4_CLIENT_STABLE, &clp->cl_flags); } /* * failure => all reset bets are off, nfserr_no_grace... */ -struct nfs4_client_reclaim * -nfs4_client_to_reclaim(const char *name, struct nfsd_net *nn) +int +nfs4_client_to_reclaim(const char *name) { unsigned int strhashval; - struct nfs4_client_reclaim *crp; + struct nfs4_client_reclaim *crp = NULL; dprintk("NFSD nfs4_client_to_reclaim NAME: %.*s\n", HEXDIR_LEN, name); crp = alloc_reclaim(); - if (crp) { - strhashval = clientstr_hashval(name); - INIT_LIST_HEAD(&crp->cr_strhash); - list_add(&crp->cr_strhash, &nn->reclaim_str_hashtbl[strhashval]); - memcpy(crp->cr_recdir, name, HEXDIR_LEN); - crp->cr_clp = NULL; - nn->reclaim_str_hashtbl_size++; - } - return crp; -} - -void -nfs4_remove_reclaim_record(struct nfs4_client_reclaim *crp, struct nfsd_net *nn) -{ - list_del(&crp->cr_strhash); - kfree(crp); - nn->reclaim_str_hashtbl_size--; + if (!crp) + return 0; + strhashval = clientstr_hashval(name); + INIT_LIST_HEAD(&crp->cr_strhash); + list_add(&crp->cr_strhash, &reclaim_str_hashtbl[strhashval]); + memcpy(crp->cr_recdir, name, HEXDIR_LEN); + reclaim_str_hashtbl_size++; + return 1; } void -nfs4_release_reclaim(struct nfsd_net *nn) +nfs4_release_reclaim(void) { struct nfs4_client_reclaim *crp = NULL; int i; for (i = 0; i < CLIENT_HASH_SIZE; i++) { - while (!list_empty(&nn->reclaim_str_hashtbl[i])) { - crp = list_entry(nn->reclaim_str_hashtbl[i].next, + while (!list_empty(&reclaim_str_hashtbl[i])) { + crp = list_entry(reclaim_str_hashtbl[i].next, struct nfs4_client_reclaim, cr_strhash); - nfs4_remove_reclaim_record(crp, nn); + list_del(&crp->cr_strhash); + kfree(crp); + reclaim_str_hashtbl_size--; } } - WARN_ON_ONCE(nn->reclaim_str_hashtbl_size); + BUG_ON(reclaim_str_hashtbl_size); } /* * called from OPEN, CLAIM_PREVIOUS with a new clientid. */ struct nfs4_client_reclaim * -nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn) +nfsd4_find_reclaim_client(struct nfs4_client *clp) { unsigned int strhashval; struct nfs4_client_reclaim *crp = NULL; - dprintk("NFSD: nfs4_find_reclaim_client for recdir %s\n", recdir); + dprintk("NFSD: nfs4_find_reclaim_client for %.*s with recdir %s\n", + clp->cl_name.len, clp->cl_name.data, + clp->cl_recdir); - strhashval = clientstr_hashval(recdir); - list_for_each_entry(crp, &nn->reclaim_str_hashtbl[strhashval], cr_strhash) { - if (same_name(crp->cr_recdir, recdir)) { + /* find clp->cl_name in reclaim_str_hashtbl */ + strhashval = clientstr_hashval(clp->cl_recdir); + list_for_each_entry(crp, &reclaim_str_hashtbl[strhashval], cr_strhash) { + if (same_name(crp->cr_recdir, clp->cl_recdir)) { return crp; } } @@ -4593,12 +4543,12 @@ nfsd4_find_reclaim_client(const char *recdir, struct nfsd_net *nn) * Called from OPEN. Look for clientid in reclaim list. */ __be32 -nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn) +nfs4_check_open_reclaim(clientid_t *clid, bool sessions) { struct nfs4_client *clp; /* find clientid in conf_id_hashtbl */ - clp = find_confirmed_client(clid, sessions, nn); + clp = find_confirmed_client(clid, sessions); if (clp == NULL) return nfserr_reclaim_bad; @@ -4607,177 +4557,124 @@ nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn) #ifdef CONFIG_NFSD_FAULT_INJECTION -u64 nfsd_forget_client(struct nfs4_client *clp, u64 max) +void nfsd_forget_clients(u64 num) { - expire_client(clp); - return 1; + struct nfs4_client *clp, *next; + int count = 0; + + nfs4_lock_state(); + list_for_each_entry_safe(clp, next, &client_lru, cl_lru) { + expire_client(clp); + if (++count == num) + break; + } + nfs4_unlock_state(); + + printk(KERN_INFO "NFSD: Forgot %d clients", count); } -u64 nfsd_print_client(struct nfs4_client *clp, u64 num) +static void release_lockowner_sop(struct nfs4_stateowner *sop) { - char buf[INET6_ADDRSTRLEN]; - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); - printk(KERN_INFO "NFS Client: %s\n", buf); - return 1; + release_lockowner(lockowner(sop)); } -static void nfsd_print_count(struct nfs4_client *clp, unsigned int count, - const char *type) +static void release_openowner_sop(struct nfs4_stateowner *sop) { - char buf[INET6_ADDRSTRLEN]; - rpc_ntop((struct sockaddr *)&clp->cl_addr, buf, sizeof(buf)); - printk(KERN_INFO "NFS Client: %s has %u %s\n", buf, count, type); + release_openowner(openowner(sop)); } -static u64 nfsd_foreach_client_lock(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_lockowner *)) +static int nfsd_release_n_owners(u64 num, bool is_open_owner, + void (*release_sop)(struct nfs4_stateowner *)) { - struct nfs4_openowner *oop; - struct nfs4_lockowner *lop, *lo_next; - struct nfs4_ol_stateid *stp, *st_next; - u64 count = 0; + int i, count = 0; + struct nfs4_stateowner *sop, *next; - list_for_each_entry(oop, &clp->cl_openowners, oo_perclient) { - list_for_each_entry_safe(stp, st_next, &oop->oo_owner.so_stateids, st_perstateowner) { - list_for_each_entry_safe(lop, lo_next, &stp->st_lockowners, lo_perstateid) { - if (func) - func(lop); - if (++count == max) - return count; - } + for (i = 0; i < OWNER_HASH_SIZE; i++) { + list_for_each_entry_safe(sop, next, &ownerstr_hashtbl[i], so_strhash) { + if (sop->so_is_open_owner != is_open_owner) + continue; + release_sop(sop); + if (++count == num) + return count; } } - return count; } -u64 nfsd_forget_client_locks(struct nfs4_client *clp, u64 max) +void nfsd_forget_locks(u64 num) { - return nfsd_foreach_client_lock(clp, max, release_lockowner); -} - -u64 nfsd_print_client_locks(struct nfs4_client *clp, u64 max) -{ - u64 count = nfsd_foreach_client_lock(clp, max, NULL); - nfsd_print_count(clp, count, "locked files"); - return count; -} + int count; -static u64 nfsd_foreach_client_open(struct nfs4_client *clp, u64 max, void (*func)(struct nfs4_openowner *)) -{ - struct nfs4_openowner *oop, *next; - u64 count = 0; - - list_for_each_entry_safe(oop, next, &clp->cl_openowners, oo_perclient) { - if (func) - func(oop); - if (++count == max) - break; - } + nfs4_lock_state(); + count = nfsd_release_n_owners(num, false, release_lockowner_sop); + nfs4_unlock_state(); - return count; + printk(KERN_INFO "NFSD: Forgot %d locks", count); } -u64 nfsd_forget_client_openowners(struct nfs4_client *clp, u64 max) +void nfsd_forget_openowners(u64 num) { - return nfsd_foreach_client_open(clp, max, release_openowner); -} + int count; -u64 nfsd_print_client_openowners(struct nfs4_client *clp, u64 max) -{ - u64 count = nfsd_foreach_client_open(clp, max, NULL); - nfsd_print_count(clp, count, "open files"); - return count; + nfs4_lock_state(); + count = nfsd_release_n_owners(num, true, release_openowner_sop); + nfs4_unlock_state(); + + printk(KERN_INFO "NFSD: Forgot %d open owners", count); } -static u64 nfsd_find_all_delegations(struct nfs4_client *clp, u64 max, - struct list_head *victims) +static int nfsd_process_n_delegations(u64 num, struct list_head *list) { - struct nfs4_delegation *dp, *next; - u64 count = 0; + int i, count = 0; + struct nfs4_file *fp, *fnext; + struct nfs4_delegation *dp, *dnext; - list_for_each_entry_safe(dp, next, &clp->cl_delegations, dl_perclnt) { - if (victims) - list_move(&dp->dl_recall_lru, victims); - if (++count == max) - break; + for (i = 0; i < FILE_HASH_SIZE; i++) { + list_for_each_entry_safe(fp, fnext, &file_hashtbl[i], fi_hash) { + list_for_each_entry_safe(dp, dnext, &fp->fi_delegations, dl_perfile) { + list_move(&dp->dl_recall_lru, list); + if (++count == num) + return count; + } + } } + return count; } -u64 nfsd_forget_client_delegations(struct nfs4_client *clp, u64 max) +void nfsd_forget_delegations(u64 num) { - struct nfs4_delegation *dp, *next; + unsigned int count; LIST_HEAD(victims); - u64 count; + struct nfs4_delegation *dp, *dnext; spin_lock(&recall_lock); - count = nfsd_find_all_delegations(clp, max, &victims); + count = nfsd_process_n_delegations(num, &victims); spin_unlock(&recall_lock); - list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) + nfs4_lock_state(); + list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) unhash_delegation(dp); + nfs4_unlock_state(); - return count; + printk(KERN_INFO "NFSD: Forgot %d delegations", count); } -u64 nfsd_recall_client_delegations(struct nfs4_client *clp, u64 max) +void nfsd_recall_delegations(u64 num) { - struct nfs4_delegation *dp, *next; + unsigned int count; LIST_HEAD(victims); - u64 count; + struct nfs4_delegation *dp, *dnext; spin_lock(&recall_lock); - count = nfsd_find_all_delegations(clp, max, &victims); - list_for_each_entry_safe(dp, next, &victims, dl_recall_lru) + count = nfsd_process_n_delegations(num, &victims); + list_for_each_entry_safe(dp, dnext, &victims, dl_recall_lru) { + list_del(&dp->dl_recall_lru); nfsd_break_one_deleg(dp); - spin_unlock(&recall_lock); - - return count; -} - -u64 nfsd_print_client_delegations(struct nfs4_client *clp, u64 max) -{ - u64 count = 0; - - spin_lock(&recall_lock); - count = nfsd_find_all_delegations(clp, max, NULL); - spin_unlock(&recall_lock); - - nfsd_print_count(clp, count, "delegations"); - return count; -} - -u64 nfsd_for_n_state(u64 max, u64 (*func)(struct nfs4_client *, u64)) -{ - struct nfs4_client *clp, *next; - u64 count = 0; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return 0; - - list_for_each_entry_safe(clp, next, &nn->client_lru, cl_lru) { - count += func(clp, max - count); - if ((max != 0) && (count >= max)) - break; } + spin_unlock(&recall_lock); - return count; -} - -struct nfs4_client *nfsd_find_client(struct sockaddr_storage *addr, size_t addr_size) -{ - struct nfs4_client *clp; - struct nfsd_net *nn = net_generic(current->nsproxy->net_ns, nfsd_net_id); - - if (!nfsd_netns_ready(nn)) - return NULL; - - list_for_each_entry(clp, &nn->client_lru, cl_lru) { - if (memcmp(&clp->cl_addr, addr, addr_size) == 0) - return clp; - } - return NULL; + printk(KERN_INFO "NFSD: Recalled %d delegations", count); } #endif /* CONFIG_NFSD_FAULT_INJECTION */ @@ -4789,10 +4686,27 @@ nfs4_state_init(void) { int i; + for (i = 0; i < CLIENT_HASH_SIZE; i++) { + INIT_LIST_HEAD(&conf_id_hashtbl[i]); + INIT_LIST_HEAD(&conf_str_hashtbl[i]); + INIT_LIST_HEAD(&unconf_str_hashtbl[i]); + INIT_LIST_HEAD(&unconf_id_hashtbl[i]); + INIT_LIST_HEAD(&reclaim_str_hashtbl[i]); + } + for (i = 0; i < SESSION_HASH_SIZE; i++) + INIT_LIST_HEAD(&sessionid_hashtbl[i]); for (i = 0; i < FILE_HASH_SIZE; i++) { INIT_LIST_HEAD(&file_hashtbl[i]); } + for (i = 0; i < OWNER_HASH_SIZE; i++) { + INIT_LIST_HEAD(&ownerstr_hashtbl[i]); + } + for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) + INIT_LIST_HEAD(&lockowner_ino_hashtbl[i]); + INIT_LIST_HEAD(&close_lru); + INIT_LIST_HEAD(&client_lru); INIT_LIST_HEAD(&del_recall_lru); + reclaim_str_hashtbl_size = 0; } /* @@ -4816,100 +4730,12 @@ set_max_delegations(void) max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT); } -static int nfs4_state_create_net(struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - int i; - - nn->conf_id_hashtbl = kmalloc(sizeof(struct list_head) * - CLIENT_HASH_SIZE, GFP_KERNEL); - if (!nn->conf_id_hashtbl) - goto err; - nn->unconf_id_hashtbl = kmalloc(sizeof(struct list_head) * - CLIENT_HASH_SIZE, GFP_KERNEL); - if (!nn->unconf_id_hashtbl) - goto err_unconf_id; - nn->ownerstr_hashtbl = kmalloc(sizeof(struct list_head) * - OWNER_HASH_SIZE, GFP_KERNEL); - if (!nn->ownerstr_hashtbl) - goto err_ownerstr; - nn->lockowner_ino_hashtbl = kmalloc(sizeof(struct list_head) * - LOCKOWNER_INO_HASH_SIZE, GFP_KERNEL); - if (!nn->lockowner_ino_hashtbl) - goto err_lockowner_ino; - nn->sessionid_hashtbl = kmalloc(sizeof(struct list_head) * - SESSION_HASH_SIZE, GFP_KERNEL); - if (!nn->sessionid_hashtbl) - goto err_sessionid; - - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - INIT_LIST_HEAD(&nn->conf_id_hashtbl[i]); - INIT_LIST_HEAD(&nn->unconf_id_hashtbl[i]); - } - for (i = 0; i < OWNER_HASH_SIZE; i++) - INIT_LIST_HEAD(&nn->ownerstr_hashtbl[i]); - for (i = 0; i < LOCKOWNER_INO_HASH_SIZE; i++) - INIT_LIST_HEAD(&nn->lockowner_ino_hashtbl[i]); - for (i = 0; i < SESSION_HASH_SIZE; i++) - INIT_LIST_HEAD(&nn->sessionid_hashtbl[i]); - nn->conf_name_tree = RB_ROOT; - nn->unconf_name_tree = RB_ROOT; - INIT_LIST_HEAD(&nn->client_lru); - INIT_LIST_HEAD(&nn->close_lru); - spin_lock_init(&nn->client_lock); - - INIT_DELAYED_WORK(&nn->laundromat_work, laundromat_main); - get_net(net); - - return 0; - -err_sessionid: - kfree(nn->lockowner_ino_hashtbl); -err_lockowner_ino: - kfree(nn->ownerstr_hashtbl); -err_ownerstr: - kfree(nn->unconf_id_hashtbl); -err_unconf_id: - kfree(nn->conf_id_hashtbl); -err: - return -ENOMEM; -} - -static void -nfs4_state_destroy_net(struct net *net) -{ - int i; - struct nfs4_client *clp = NULL; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - struct rb_node *node, *tmp; - - for (i = 0; i < CLIENT_HASH_SIZE; i++) { - while (!list_empty(&nn->conf_id_hashtbl[i])) { - clp = list_entry(nn->conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); - destroy_client(clp); - } - } - - node = rb_first(&nn->unconf_name_tree); - while (node != NULL) { - tmp = node; - node = rb_next(tmp); - clp = rb_entry(tmp, struct nfs4_client, cl_namenode); - rb_erase(tmp, &nn->unconf_name_tree); - destroy_client(clp); - } - - kfree(nn->sessionid_hashtbl); - kfree(nn->lockowner_ino_hashtbl); - kfree(nn->ownerstr_hashtbl); - kfree(nn->unconf_id_hashtbl); - kfree(nn->conf_id_hashtbl); - put_net(net); -} +/* initialization to perform when the nfsd service is started: */ int -nfs4_state_start_net(struct net *net) +nfs4_state_start(void) { + struct net *net = &init_net; struct nfsd_net *nn = net_generic(net, nfsd_net_id); int ret; @@ -4920,32 +4746,18 @@ nfs4_state_start_net(struct net *net) * to that instead and then do most of the rest of this on a per-net * basis. */ - if (net != &init_net) - return -EINVAL; - - ret = nfs4_state_create_net(net); - if (ret) - return ret; + get_net(net); nfsd4_client_tracking_init(net); nn->boot_time = get_seconds(); locks_start_grace(net, &nn->nfsd4_manager); nn->grace_ended = false; - printk(KERN_INFO "NFSD: starting %ld-second grace period (net %p)\n", - nn->nfsd4_grace, net); - queue_delayed_work(laundry_wq, &nn->laundromat_work, nn->nfsd4_grace * HZ); - return 0; -} - -/* initialization to perform when the nfsd service is started: */ - -int -nfs4_state_start(void) -{ - int ret; - + printk(KERN_INFO "NFSD: starting %ld-second grace period\n", + nfsd4_grace); ret = set_callback_cred(); - if (ret) - return -ENOMEM; + if (ret) { + ret = -ENOMEM; + goto out_recovery; + } laundry_wq = create_singlethread_workqueue("nfsd4"); if (laundry_wq == NULL) { ret = -ENOMEM; @@ -4954,34 +4766,39 @@ nfs4_state_start(void) ret = nfsd4_create_callback_queue(); if (ret) goto out_free_laundry; - + queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); set_max_delegations(); - return 0; - out_free_laundry: destroy_workqueue(laundry_wq); out_recovery: + nfsd4_client_tracking_exit(net); + put_net(net); return ret; } -/* should be called with the state lock held */ -void -nfs4_state_shutdown_net(struct net *net) +static void +__nfs4_state_shutdown(void) { + int i; + struct nfs4_client *clp = NULL; struct nfs4_delegation *dp = NULL; struct list_head *pos, *next, reaplist; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - cancel_delayed_work_sync(&nn->laundromat_work); - locks_end_grace(&nn->nfsd4_manager); + for (i = 0; i < CLIENT_HASH_SIZE; i++) { + while (!list_empty(&conf_id_hashtbl[i])) { + clp = list_entry(conf_id_hashtbl[i].next, struct nfs4_client, cl_idhash); + destroy_client(clp); + } + while (!list_empty(&unconf_str_hashtbl[i])) { + clp = list_entry(unconf_str_hashtbl[i].next, struct nfs4_client, cl_strhash); + destroy_client(clp); + } + } INIT_LIST_HEAD(&reaplist); spin_lock(&recall_lock); list_for_each_safe(pos, next, &del_recall_lru) { dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); - if (dp->dl_stid.sc_client->net != net) - continue; list_move(&dp->dl_recall_lru, &reaplist); } spin_unlock(&recall_lock); @@ -4990,14 +4807,22 @@ nfs4_state_shutdown_net(struct net *net) unhash_delegation(dp); } - nfsd4_client_tracking_exit(net); - nfs4_state_destroy_net(net); + nfsd4_client_tracking_exit(&init_net); + put_net(&init_net); } void nfs4_state_shutdown(void) { + struct net *net = &init_net; + struct nfsd_net *nn = net_generic(net, nfsd_net_id); + + cancel_delayed_work_sync(&laundromat_work); destroy_workqueue(laundry_wq); + locks_end_grace(&nn->nfsd4_manager); + nfs4_lock_state(); + __nfs4_state_shutdown(); + nfs4_unlock_state(); nfsd4_destroy_callback_queue(); } diff --git a/trunk/fs/nfsd/nfs4xdr.c b/trunk/fs/nfsd/nfs4xdr.c index 0dc11586682f..fd548d155088 100644 --- a/trunk/fs/nfsd/nfs4xdr.c +++ b/trunk/fs/nfsd/nfs4xdr.c @@ -53,7 +53,6 @@ #include "vfs.h" #include "state.h" #include "cache.h" -#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_XDR @@ -66,17 +65,17 @@ #define NFS4_REFERRAL_FSID_MINOR 0x8000000ULL static __be32 -check_filename(char *str, int len) +check_filename(char *str, int len, __be32 err) { int i; if (len == 0) return nfserr_inval; if (isdotent(str, len)) - return nfserr_badname; + return err; for (i = 0; i < len; i++) if (str[i] == '/') - return nfserr_badname; + return err; return 0; } @@ -423,86 +422,6 @@ nfsd4_decode_access(struct nfsd4_compoundargs *argp, struct nfsd4_access *access DECODE_TAIL; } -static __be32 nfsd4_decode_cb_sec(struct nfsd4_compoundargs *argp, struct nfsd4_cb_sec *cbs) -{ - DECODE_HEAD; - u32 dummy, uid, gid; - char *machine_name; - int i; - int nr_secflavs; - - /* callback_sec_params4 */ - READ_BUF(4); - READ32(nr_secflavs); - cbs->flavor = (u32)(-1); - for (i = 0; i < nr_secflavs; ++i) { - READ_BUF(4); - READ32(dummy); - switch (dummy) { - case RPC_AUTH_NULL: - /* Nothing to read */ - if (cbs->flavor == (u32)(-1)) - cbs->flavor = RPC_AUTH_NULL; - break; - case RPC_AUTH_UNIX: - READ_BUF(8); - /* stamp */ - READ32(dummy); - - /* machine name */ - READ32(dummy); - READ_BUF(dummy); - SAVEMEM(machine_name, dummy); - - /* uid, gid */ - READ_BUF(8); - READ32(uid); - READ32(gid); - - /* more gids */ - READ_BUF(4); - READ32(dummy); - READ_BUF(dummy * 4); - if (cbs->flavor == (u32)(-1)) { - cbs->uid = uid; - cbs->gid = gid; - cbs->flavor = RPC_AUTH_UNIX; - } - break; - case RPC_AUTH_GSS: - dprintk("RPC_AUTH_GSS callback secflavor " - "not supported!\n"); - READ_BUF(8); - /* gcbp_service */ - READ32(dummy); - /* gcbp_handle_from_server */ - READ32(dummy); - READ_BUF(dummy); - p += XDR_QUADLEN(dummy); - /* gcbp_handle_from_client */ - READ_BUF(4); - READ32(dummy); - READ_BUF(dummy); - break; - default: - dprintk("Illegal callback secflavor\n"); - return nfserr_inval; - } - } - DECODE_TAIL; -} - -static __be32 nfsd4_decode_backchannel_ctl(struct nfsd4_compoundargs *argp, struct nfsd4_backchannel_ctl *bc) -{ - DECODE_HEAD; - - READ_BUF(4); - READ32(bc->bc_cb_program); - nfsd4_decode_cb_sec(argp, &bc->bc_cb_sec); - - DECODE_TAIL; -} - static __be32 nfsd4_decode_bind_conn_to_session(struct nfsd4_compoundargs *argp, struct nfsd4_bind_conn_to_session *bcts) { DECODE_HEAD; @@ -571,7 +490,7 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create READ32(create->cr_namelen); READ_BUF(create->cr_namelen); SAVEMEM(create->cr_name, create->cr_namelen); - if ((status = check_filename(create->cr_name, create->cr_namelen))) + if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval))) return status; status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr, @@ -603,7 +522,7 @@ nfsd4_decode_link(struct nfsd4_compoundargs *argp, struct nfsd4_link *link) READ32(link->li_namelen); READ_BUF(link->li_namelen); SAVEMEM(link->li_name, link->li_namelen); - if ((status = check_filename(link->li_name, link->li_namelen))) + if ((status = check_filename(link->li_name, link->li_namelen, nfserr_inval))) return status; DECODE_TAIL; @@ -697,7 +616,7 @@ nfsd4_decode_lookup(struct nfsd4_compoundargs *argp, struct nfsd4_lookup *lookup READ32(lookup->lo_len); READ_BUF(lookup->lo_len); SAVEMEM(lookup->lo_name, lookup->lo_len); - if ((status = check_filename(lookup->lo_name, lookup->lo_len))) + if ((status = check_filename(lookup->lo_name, lookup->lo_len, nfserr_noent))) return status; DECODE_TAIL; @@ -861,7 +780,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) READ32(open->op_fname.len); READ_BUF(open->op_fname.len); SAVEMEM(open->op_fname.data, open->op_fname.len); - if ((status = check_filename(open->op_fname.data, open->op_fname.len))) + if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) return status; break; case NFS4_OPEN_CLAIM_PREVIOUS: @@ -876,7 +795,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open) READ32(open->op_fname.len); READ_BUF(open->op_fname.len); SAVEMEM(open->op_fname.data, open->op_fname.len); - if ((status = check_filename(open->op_fname.data, open->op_fname.len))) + if ((status = check_filename(open->op_fname.data, open->op_fname.len, nfserr_inval))) return status; break; case NFS4_OPEN_CLAIM_FH: @@ -988,7 +907,7 @@ nfsd4_decode_remove(struct nfsd4_compoundargs *argp, struct nfsd4_remove *remove READ32(remove->rm_namelen); READ_BUF(remove->rm_namelen); SAVEMEM(remove->rm_name, remove->rm_namelen); - if ((status = check_filename(remove->rm_name, remove->rm_namelen))) + if ((status = check_filename(remove->rm_name, remove->rm_namelen, nfserr_noent))) return status; DECODE_TAIL; @@ -1006,9 +925,9 @@ nfsd4_decode_rename(struct nfsd4_compoundargs *argp, struct nfsd4_rename *rename READ32(rename->rn_tnamelen); READ_BUF(rename->rn_tnamelen); SAVEMEM(rename->rn_tname, rename->rn_tnamelen); - if ((status = check_filename(rename->rn_sname, rename->rn_snamelen))) + if ((status = check_filename(rename->rn_sname, rename->rn_snamelen, nfserr_noent))) return status; - if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen))) + if ((status = check_filename(rename->rn_tname, rename->rn_tnamelen, nfserr_inval))) return status; DECODE_TAIL; @@ -1035,7 +954,8 @@ nfsd4_decode_secinfo(struct nfsd4_compoundargs *argp, READ32(secinfo->si_namelen); READ_BUF(secinfo->si_namelen); SAVEMEM(secinfo->si_name, secinfo->si_namelen); - status = check_filename(secinfo->si_name, secinfo->si_namelen); + status = check_filename(secinfo->si_name, secinfo->si_namelen, + nfserr_noent); if (status) return status; DECODE_TAIL; @@ -1106,14 +1026,31 @@ nfsd4_decode_setclientid_confirm(struct nfsd4_compoundargs *argp, struct nfsd4_s static __be32 nfsd4_decode_verify(struct nfsd4_compoundargs *argp, struct nfsd4_verify *verify) { +#if 0 + struct nfsd4_compoundargs save = { + .p = argp->p, + .end = argp->end, + .rqstp = argp->rqstp, + }; + u32 ve_bmval[2]; + struct iattr ve_iattr; /* request */ + struct nfs4_acl *ve_acl; /* request */ +#endif DECODE_HEAD; if ((status = nfsd4_decode_bitmap(argp, verify->ve_bmval))) goto out; /* For convenience's sake, we compare raw xdr'd attributes in - * nfsd4_proc_verify */ - + * nfsd4_proc_verify; however we still decode here just to return + * correct error in case of bad xdr. */ +#if 0 + status = nfsd4_decode_fattr(ve_bmval, &ve_iattr, &ve_acl); + if (status == nfserr_inval) { + status = nfserrno(status); + goto out; + } +#endif READ_BUF(4); READ32(verify->ve_attrlen); READ_BUF(verify->ve_attrlen); @@ -1126,6 +1063,7 @@ static __be32 nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) { int avail; + int v; int len; DECODE_HEAD; @@ -1149,26 +1087,27 @@ nfsd4_decode_write(struct nfsd4_compoundargs *argp, struct nfsd4_write *write) __FILE__, __LINE__); goto xdr_error; } - write->wr_head.iov_base = p; - write->wr_head.iov_len = avail; - WARN_ON(avail != (XDR_QUADLEN(avail) << 2)); - write->wr_pagelist = argp->pagelist; - - len = XDR_QUADLEN(write->wr_buflen) << 2; - if (len >= avail) { - int pages; - - len -= avail; - - pages = len >> PAGE_SHIFT; - argp->pagelist += pages; - argp->pagelen -= pages * PAGE_SIZE; - len -= pages * PAGE_SIZE; - - argp->p = (__be32 *)page_address(argp->pagelist[0]); - argp->end = argp->p + XDR_QUADLEN(PAGE_SIZE); + argp->rqstp->rq_vec[0].iov_base = p; + argp->rqstp->rq_vec[0].iov_len = avail; + v = 0; + len = write->wr_buflen; + while (len > argp->rqstp->rq_vec[v].iov_len) { + len -= argp->rqstp->rq_vec[v].iov_len; + v++; + argp->rqstp->rq_vec[v].iov_base = page_address(argp->pagelist[0]); + argp->pagelist++; + if (argp->pagelen >= PAGE_SIZE) { + argp->rqstp->rq_vec[v].iov_len = PAGE_SIZE; + argp->pagelen -= PAGE_SIZE; + } else { + argp->rqstp->rq_vec[v].iov_len = argp->pagelen; + argp->pagelen -= len; + } } - argp->p += XDR_QUADLEN(len); + argp->end = (__be32*) (argp->rqstp->rq_vec[v].iov_base + argp->rqstp->rq_vec[v].iov_len); + argp->p = (__be32*) (argp->rqstp->rq_vec[v].iov_base + (XDR_QUADLEN(len) << 2)); + argp->rqstp->rq_vec[v].iov_len = len; + write->wr_vlen = v+1; DECODE_TAIL; } @@ -1298,7 +1237,11 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, struct nfsd4_create_session *sess) { DECODE_HEAD; + u32 dummy; + char *machine_name; + int i; + int nr_secflavs; READ_BUF(16); COPYMEM(&sess->clientid, 8); @@ -1339,9 +1282,58 @@ nfsd4_decode_create_session(struct nfsd4_compoundargs *argp, goto xdr_error; } - READ_BUF(4); + READ_BUF(8); READ32(sess->callback_prog); - nfsd4_decode_cb_sec(argp, &sess->cb_sec); + + /* callback_sec_params4 */ + READ32(nr_secflavs); + for (i = 0; i < nr_secflavs; ++i) { + READ_BUF(4); + READ32(dummy); + switch (dummy) { + case RPC_AUTH_NULL: + /* Nothing to read */ + break; + case RPC_AUTH_UNIX: + READ_BUF(8); + /* stamp */ + READ32(dummy); + + /* machine name */ + READ32(dummy); + READ_BUF(dummy); + SAVEMEM(machine_name, dummy); + + /* uid, gid */ + READ_BUF(8); + READ32(sess->uid); + READ32(sess->gid); + + /* more gids */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy * 4); + break; + case RPC_AUTH_GSS: + dprintk("RPC_AUTH_GSS callback secflavor " + "not supported!\n"); + READ_BUF(8); + /* gcbp_service */ + READ32(dummy); + /* gcbp_handle_from_server */ + READ32(dummy); + READ_BUF(dummy); + p += XDR_QUADLEN(dummy); + /* gcbp_handle_from_client */ + READ_BUF(4); + READ32(dummy); + READ_BUF(dummy); + break; + default: + dprintk("Illegal callback secflavor\n"); + return nfserr_inval; + } + } DECODE_TAIL; } @@ -1536,7 +1528,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { [OP_RELEASE_LOCKOWNER] = (nfsd4_dec)nfsd4_decode_notsupp, /* new operations for NFSv4.1 */ - [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_backchannel_ctl, + [OP_BACKCHANNEL_CTL] = (nfsd4_dec)nfsd4_decode_notsupp, [OP_BIND_CONN_TO_SESSION]= (nfsd4_dec)nfsd4_decode_bind_conn_to_session, [OP_EXCHANGE_ID] = (nfsd4_dec)nfsd4_decode_exchange_id, [OP_CREATE_SESSION] = (nfsd4_dec)nfsd4_decode_create_session, @@ -1576,6 +1568,12 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) bool cachethis = false; int i; + /* + * XXX: According to spec, we should check the tag + * for UTF-8 compliance. I'm postponing this for + * now because it seems that some clients do use + * binary tags. + */ READ_BUF(4); READ32(argp->taglen); READ_BUF(argp->taglen + 8); @@ -1605,8 +1603,38 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp) op = &argp->ops[i]; op->replay = NULL; - READ_BUF(4); - READ32(op->opnum); + /* + * We can't use READ_BUF() here because we need to handle + * a missing opcode as an OP_WRITE + 1. So we need to check + * to see if we're truly at the end of our buffer or if there + * is another page we need to flip to. + */ + + if (argp->p == argp->end) { + if (argp->pagelen < 4) { + /* There isn't an opcode still on the wire */ + op->opnum = OP_WRITE + 1; + op->status = nfserr_bad_xdr; + argp->opcnt = i+1; + break; + } + + /* + * False alarm. We just hit a page boundary, but there + * is still data available. Move pointer across page + * boundary. *snip from READ_BUF* + */ + argp->p = page_address(argp->pagelist[0]); + argp->pagelist++; + if (argp->pagelen < PAGE_SIZE) { + argp->end = argp->p + (argp->pagelen>>2); + argp->pagelen = 0; + } else { + argp->end = argp->p + (PAGE_SIZE>>2); + argp->pagelen -= PAGE_SIZE; + } + } + op->opnum = ntohl(*argp->p++); if (op->opnum >= FIRST_NFS4_OP && op->opnum <= LAST_NFS4_OP) op->status = ops->decoders[op->opnum](argp, &op->u); @@ -1986,22 +2014,6 @@ static __be32 fattr_handle_absent_fs(u32 *bmval0, u32 *bmval1, u32 *rdattr_err) return 0; } - -static int get_parent_attributes(struct svc_export *exp, struct kstat *stat) -{ - struct path path = exp->ex_path; - int err; - - path_get(&path); - while (follow_up(&path)) { - if (path.dentry != path.mnt->mnt_root) - break; - } - err = vfs_getattr(path.mnt, path.dentry, stat); - path_put(&path); - return err; -} - /* * Note: @fhp can be NULL; in this case, we might have to compose the filehandle * ourselves. @@ -2036,7 +2048,6 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, .mnt = exp->ex_path.mnt, .dentry = dentry, }; - struct nfsd_net *nn = net_generic(SVC_NET(rqstp), nfsd_net_id); BUG_ON(bmval1 & NFSD_WRITEONLY_ATTRS_WORD1); BUG_ON(bmval0 & ~nfsd_suppattrs0(minorversion)); @@ -2197,7 +2208,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, if (bmval0 & FATTR4_WORD0_LEASE_TIME) { if ((buflen -= 4) < 0) goto out_resource; - WRITE32(nn->nfsd4_lease); + WRITE32(nfsd4_lease); } if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { if ((buflen -= 4) < 0) @@ -2419,8 +2430,18 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, * and this is the root of a cross-mounted filesystem. */ if (ignore_crossmnt == 0 && - dentry == exp->ex_path.mnt->mnt_root) - get_parent_attributes(exp, &stat); + dentry == exp->ex_path.mnt->mnt_root) { + struct path path = exp->ex_path; + path_get(&path); + while (follow_up(&path)) { + if (path.dentry != path.mnt->mnt_root) + break; + } + err = vfs_getattr(path.mnt, path.dentry, &stat); + path_put(&path); + if (err) + goto out_nfserr; + } WRITE64(stat.ino); } if (bmval2 & FATTR4_WORD2_SUPPATTR_EXCLCREAT) { @@ -2906,8 +2927,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_read *read) { u32 eof; - int v; - struct page *page; + int v, pn; unsigned long maxcount; long len; __be32 *p; @@ -2926,15 +2946,11 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr, len = maxcount; v = 0; while (len > 0) { - page = *(resp->rqstp->rq_next_page); - if (!page) { /* ran out of pages */ - maxcount -= len; - break; - } - resp->rqstp->rq_vec[v].iov_base = page_address(page); + pn = resp->rqstp->rq_resused++; + resp->rqstp->rq_vec[v].iov_base = + page_address(resp->rqstp->rq_respages[pn]); resp->rqstp->rq_vec[v].iov_len = len < PAGE_SIZE ? len : PAGE_SIZE; - resp->rqstp->rq_next_page++; v++; len -= PAGE_SIZE; } @@ -2980,10 +2996,8 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd return nfserr; if (resp->xbuf->page_len) return nfserr_resource; - if (!*resp->rqstp->rq_next_page) - return nfserr_resource; - page = page_address(*(resp->rqstp->rq_next_page++)); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); maxcount = PAGE_SIZE; RESERVE_SPACE(4); @@ -3031,8 +3045,6 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 return nfserr; if (resp->xbuf->page_len) return nfserr_resource; - if (!*resp->rqstp->rq_next_page) - return nfserr_resource; RESERVE_SPACE(NFS4_VERIFIER_SIZE); savep = p; @@ -3059,7 +3071,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 goto err_no_verf; } - page = page_address(*(resp->rqstp->rq_next_page++)); + page = page_address(resp->rqstp->rq_respages[resp->rqstp->rq_resused++]); readdir->common.err = 0; readdir->buflen = maxcount; readdir->buffer = page; @@ -3082,8 +3094,8 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4 p = readdir->buffer; *p++ = 0; /* no more entries */ *p++ = htonl(readdir->common.err == nfserr_eof); - resp->xbuf->page_len = ((char*)p) - - (char*)page_address(*(resp->rqstp->rq_next_page-1)); + resp->xbuf->page_len = ((char*)p) - (char*)page_address( + resp->rqstp->rq_respages[resp->rqstp->rq_resused-1]); /* Use rest of head for padding and remaining ops: */ resp->xbuf->tail[0].iov_base = tailbase; diff --git a/trunk/fs/nfsd/nfsctl.c b/trunk/fs/nfsd/nfsctl.c index 74934284d9a7..dab350dfc376 100644 --- a/trunk/fs/nfsd/nfsctl.c +++ b/trunk/fs/nfsd/nfsctl.c @@ -19,7 +19,7 @@ #include "idmap.h" #include "nfsd.h" #include "cache.h" -#include "state.h" +#include "fault_inject.h" #include "netns.h" /* @@ -186,6 +186,9 @@ static struct file_operations supported_enctypes_ops = { }; #endif /* CONFIG_SUNRPC_GSS or CONFIG_SUNRPC_GSS_MODULE */ +extern int nfsd_pool_stats_open(struct inode *inode, struct file *file); +extern int nfsd_pool_stats_release(struct inode *inode, struct file *file); + static const struct file_operations pool_stats_operations = { .open = nfsd_pool_stats_open, .read = seq_read, @@ -396,8 +399,6 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) { char *mesg = buf; int rv; - struct net *net = &init_net; - if (size > 0) { int newthreads; rv = get_int(&mesg, &newthreads); @@ -405,11 +406,11 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size) return rv; if (newthreads < 0) return -EINVAL; - rv = nfsd_svc(newthreads, net); + rv = nfsd_svc(newthreads); if (rv < 0) return rv; } else - rv = nfsd_nrthreads(net); + rv = nfsd_nrthreads(); return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n", rv); } @@ -447,10 +448,9 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) int len; int npools; int *nthreads; - struct net *net = &init_net; mutex_lock(&nfsd_mutex); - npools = nfsd_nrpools(net); + npools = nfsd_nrpools(); if (npools == 0) { /* * NFS is shut down. The admin can start it by @@ -478,12 +478,12 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size) if (nthreads[i] < 0) goto out_free; } - rv = nfsd_set_nrthreads(i, nthreads, net); + rv = nfsd_set_nrthreads(i, nthreads); if (rv) goto out_free; } - rv = nfsd_get_nrthreads(npools, nthreads, net); + rv = nfsd_get_nrthreads(npools, nthreads); if (rv) goto out_free; @@ -510,13 +510,11 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size) unsigned minor; ssize_t tlen = 0; char *sep; - struct net *net = &init_net; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); if (size>0) { - if (nn->nfsd_serv) + if (nfsd_serv) /* Cannot change versions without updating - * nn->nfsd_serv->sv_xdrsize, and reallocing + * nfsd_serv->sv_xdrsize, and reallocing * rq_argp and rq_resp */ return -EBUSY; @@ -647,13 +645,11 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size) * Zero-length write. Return a list of NFSD's current listener * transports. */ -static ssize_t __write_ports_names(char *buf, struct net *net) +static ssize_t __write_ports_names(char *buf) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - if (nn->nfsd_serv == NULL) + if (nfsd_serv == NULL) return 0; - return svc_xprt_names(nn->nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); + return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT); } /* @@ -661,28 +657,28 @@ static ssize_t __write_ports_names(char *buf, struct net *net) * a socket of a supported family/protocol, and we use it as an * nfsd listener. */ -static ssize_t __write_ports_addfd(char *buf, struct net *net) +static ssize_t __write_ports_addfd(char *buf) { char *mesg = buf; int fd, err; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct net *net = &init_net; err = get_int(&mesg, &fd); if (err != 0 || fd < 0) return -EINVAL; - err = nfsd_create_serv(net); + err = nfsd_create_serv(); if (err != 0) return err; - err = svc_addsock(nn->nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); + err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT); if (err < 0) { nfsd_destroy(net); return err; } /* Decrease the count, but don't shut down the service */ - nn->nfsd_serv->sv_nrthreads--; + nfsd_serv->sv_nrthreads--; return err; } @@ -690,12 +686,12 @@ static ssize_t __write_ports_addfd(char *buf, struct net *net) * A transport listener is added by writing it's transport name and * a port number. */ -static ssize_t __write_ports_addxprt(char *buf, struct net *net) +static ssize_t __write_ports_addxprt(char *buf) { char transport[16]; struct svc_xprt *xprt; int port, err; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct net *net = &init_net; if (sscanf(buf, "%15s %5u", transport, &port) != 2) return -EINVAL; @@ -703,25 +699,25 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net) if (port < 1 || port > USHRT_MAX) return -EINVAL; - err = nfsd_create_serv(net); + err = nfsd_create_serv(); if (err != 0) return err; - err = svc_create_xprt(nn->nfsd_serv, transport, net, + err = svc_create_xprt(nfsd_serv, transport, net, PF_INET, port, SVC_SOCK_ANONYMOUS); if (err < 0) goto out_err; - err = svc_create_xprt(nn->nfsd_serv, transport, net, + err = svc_create_xprt(nfsd_serv, transport, net, PF_INET6, port, SVC_SOCK_ANONYMOUS); if (err < 0 && err != -EAFNOSUPPORT) goto out_close; /* Decrease the count, but don't shut down the service */ - nn->nfsd_serv->sv_nrthreads--; + nfsd_serv->sv_nrthreads--; return 0; out_close: - xprt = svc_find_xprt(nn->nfsd_serv, transport, net, PF_INET, port); + xprt = svc_find_xprt(nfsd_serv, transport, net, PF_INET, port); if (xprt != NULL) { svc_close_xprt(xprt); svc_xprt_put(xprt); @@ -731,17 +727,16 @@ static ssize_t __write_ports_addxprt(char *buf, struct net *net) return err; } -static ssize_t __write_ports(struct file *file, char *buf, size_t size, - struct net *net) +static ssize_t __write_ports(struct file *file, char *buf, size_t size) { if (size == 0) - return __write_ports_names(buf, net); + return __write_ports_names(buf); if (isdigit(buf[0])) - return __write_ports_addfd(buf, net); + return __write_ports_addfd(buf); if (isalpha(buf[0])) - return __write_ports_addxprt(buf, net); + return __write_ports_addxprt(buf); return -EINVAL; } @@ -792,10 +787,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size, static ssize_t write_ports(struct file *file, char *buf, size_t size) { ssize_t rv; - struct net *net = &init_net; mutex_lock(&nfsd_mutex); - rv = __write_ports(file, buf, size, net); + rv = __write_ports(file, buf, size); mutex_unlock(&nfsd_mutex); return rv; } @@ -827,9 +821,6 @@ int nfsd_max_blksize; static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) { char *mesg = buf; - struct net *net = &init_net; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - if (size > 0) { int bsize; int rv = get_int(&mesg, &bsize); @@ -844,7 +835,7 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) bsize = NFSSVC_MAXBLKSIZE; bsize &= ~(1024-1); mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv) { + if (nfsd_serv) { mutex_unlock(&nfsd_mutex); return -EBUSY; } @@ -857,14 +848,13 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) } #ifdef CONFIG_NFSD_V4 -static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, - time_t *time, struct nfsd_net *nn) +static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) { char *mesg = buf; int rv, i; if (size > 0) { - if (nn->nfsd_serv) + if (nfsd_serv) return -EBUSY; rv = get_int(&mesg, &i); if (rv) @@ -889,13 +879,12 @@ static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); } -static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, - time_t *time, struct nfsd_net *nn) +static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) { ssize_t rv; mutex_lock(&nfsd_mutex); - rv = __nfsd4_write_time(file, buf, size, time, nn); + rv = __nfsd4_write_time(file, buf, size, time); mutex_unlock(&nfsd_mutex); return rv; } @@ -923,8 +912,7 @@ static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, */ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn); + return nfsd4_write_time(file, buf, size, &nfsd4_lease); } /** @@ -939,19 +927,17 @@ static ssize_t write_leasetime(struct file *file, char *buf, size_t size) */ static ssize_t write_gracetime(struct file *file, char *buf, size_t size) { - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); - return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn); + return nfsd4_write_time(file, buf, size, &nfsd4_grace); } -static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, - struct nfsd_net *nn) +static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size) { char *mesg = buf; char *recdir; int len, status; if (size > 0) { - if (nn->nfsd_serv) + if (nfsd_serv) return -EBUSY; if (size > PATH_MAX || buf[size-1] != '\n') return -EINVAL; @@ -995,10 +981,9 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size, static ssize_t write_recoverydir(struct file *file, char *buf, size_t size) { ssize_t rv; - struct nfsd_net *nn = net_generic(&init_net, nfsd_net_id); mutex_lock(&nfsd_mutex); - rv = __write_recoverydir(file, buf, size, nn); + rv = __write_recoverydir(file, buf, size); mutex_unlock(&nfsd_mutex); return rv; } @@ -1078,7 +1063,6 @@ int nfsd_net_id; static __net_init int nfsd_init_net(struct net *net) { int retval; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); retval = nfsd_export_init(net); if (retval) @@ -1086,8 +1070,6 @@ static __net_init int nfsd_init_net(struct net *net) retval = nfsd_idmap_init(net); if (retval) goto out_idmap_error; - nn->nfsd4_lease = 90; /* default lease time */ - nn->nfsd4_grace = 90; return 0; out_idmap_error: diff --git a/trunk/fs/nfsd/nfsd.h b/trunk/fs/nfsd/nfsd.h index de23db255c69..80d5ce40aadb 100644 --- a/trunk/fs/nfsd/nfsd.h +++ b/trunk/fs/nfsd/nfsd.h @@ -55,6 +55,7 @@ extern struct svc_version nfsd_version2, nfsd_version3, nfsd_version4; extern u32 nfsd_supported_minorversion; extern struct mutex nfsd_mutex; +extern struct svc_serv *nfsd_serv; extern spinlock_t nfsd_drc_lock; extern unsigned int nfsd_drc_max_mem; extern unsigned int nfsd_drc_mem_used; @@ -64,17 +65,26 @@ extern const struct seq_operations nfs_exports_op; /* * Function prototypes. */ -int nfsd_svc(int nrservs, struct net *net); +int nfsd_svc(int nrservs); int nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp); -int nfsd_nrthreads(struct net *); -int nfsd_nrpools(struct net *); -int nfsd_get_nrthreads(int n, int *, struct net *); -int nfsd_set_nrthreads(int n, int *, struct net *); +int nfsd_nrthreads(void); +int nfsd_nrpools(void); +int nfsd_get_nrthreads(int n, int *); +int nfsd_set_nrthreads(int n, int *); int nfsd_pool_stats_open(struct inode *, struct file *); int nfsd_pool_stats_release(struct inode *, struct file *); -void nfsd_destroy(struct net *net); +static inline void nfsd_destroy(struct net *net) +{ + int destroy = (nfsd_serv->sv_nrthreads == 1); + + if (destroy) + svc_shutdown_net(nfsd_serv, net); + svc_destroy(nfsd_serv); + if (destroy) + nfsd_serv = NULL; +} #if defined(CONFIG_NFSD_V2_ACL) || defined(CONFIG_NFSD_V3_ACL) #ifdef CONFIG_NFSD_V2_ACL @@ -93,7 +103,7 @@ enum vers_op {NFSD_SET, NFSD_CLEAR, NFSD_TEST, NFSD_AVAIL }; int nfsd_vers(int vers, enum vers_op change); int nfsd_minorversion(u32 minorversion, enum vers_op change); void nfsd_reset_versions(void); -int nfsd_create_serv(struct net *net); +int nfsd_create_serv(void); extern int nfsd_max_blksize; @@ -111,9 +121,7 @@ void nfs4_state_init(void); int nfsd4_init_slabs(void); void nfsd4_free_slabs(void); int nfs4_state_start(void); -int nfs4_state_start_net(struct net *net); void nfs4_state_shutdown(void); -void nfs4_state_shutdown_net(struct net *net); void nfs4_reset_lease(time_t leasetime); int nfs4_reset_recoverydir(char *recdir); char * nfs4_recoverydir(void); @@ -122,9 +130,7 @@ static inline void nfs4_state_init(void) { } static inline int nfsd4_init_slabs(void) { return 0; } static inline void nfsd4_free_slabs(void) { } static inline int nfs4_state_start(void) { return 0; } -static inline int nfs4_state_start_net(struct net *net) { return 0; } static inline void nfs4_state_shutdown(void) { } -static inline void nfs4_state_shutdown_net(struct net *net) { } static inline void nfs4_reset_lease(time_t leasetime) { } static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } static inline char * nfs4_recoverydir(void) {return NULL; } @@ -259,8 +265,16 @@ void nfsd_lockd_shutdown(void); /* Check for dir entries '.' and '..' */ #define isdotent(n, l) (l < 3 && n[0] == '.' && (l == 1 || n[1] == '.')) +/* + * Time of server startup + */ +extern struct timeval nfssvc_boot; + #ifdef CONFIG_NFSD_V4 +extern time_t nfsd4_lease; +extern time_t nfsd4_grace; + /* before processing a COMPOUND operation, we have to check that there * is enough space in the buffer for XDR encode to succeed. otherwise, * we might process an operation with side effects, and be unable to diff --git a/trunk/fs/nfsd/nfsfh.c b/trunk/fs/nfsd/nfsfh.c index 814afaa4458a..032af381b3aa 100644 --- a/trunk/fs/nfsd/nfsfh.c +++ b/trunk/fs/nfsd/nfsfh.c @@ -572,7 +572,7 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry, if (inode) _fh_update(fhp, exp, dentry); - if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) { + if (fhp->fh_handle.fh_fileid_type == 255) { fh_put(fhp); return nfserr_opnotsupp; } @@ -603,7 +603,7 @@ fh_update(struct svc_fh *fhp) goto out; _fh_update(fhp, fhp->fh_export, dentry); - if (fhp->fh_handle.fh_fileid_type == FILEID_INVALID) + if (fhp->fh_handle.fh_fileid_type == 255) return nfserr_opnotsupp; } out: diff --git a/trunk/fs/nfsd/nfssvc.c b/trunk/fs/nfsd/nfssvc.c index cee62ab9d4a3..2013aa001dab 100644 --- a/trunk/fs/nfsd/nfssvc.c +++ b/trunk/fs/nfsd/nfssvc.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -21,19 +22,19 @@ #include "nfsd.h" #include "cache.h" #include "vfs.h" -#include "netns.h" #define NFSDDBG_FACILITY NFSDDBG_SVC extern struct svc_program nfsd_program; static int nfsd(void *vrqstp); +struct timeval nfssvc_boot; /* - * nfsd_mutex protects nn->nfsd_serv -- both the pointer itself and the members + * nfsd_mutex protects nfsd_serv -- both the pointer itself and the members * of the svc_serv struct. In particular, ->sv_nrthreads but also to some * extent ->sv_temp_socks and ->sv_permsocks. It also protects nfsdstats.th_cnt * - * If (out side the lock) nn->nfsd_serv is non-NULL, then it must point to a + * If (out side the lock) nfsd_serv is non-NULL, then it must point to a * properly initialised 'struct svc_serv' with ->sv_nrthreads > 0. That number * of nfsd threads must exist and each must listed in ->sp_all_threads in each * entry of ->sv_pools[]. @@ -51,6 +52,7 @@ static int nfsd(void *vrqstp); * nfsd_versions */ DEFINE_MUTEX(nfsd_mutex); +struct svc_serv *nfsd_serv; /* * nfsd_drc_lock protects nfsd_drc_max_pages and nfsd_drc_pages_used. @@ -171,32 +173,28 @@ int nfsd_minorversion(u32 minorversion, enum vers_op change) */ #define NFSD_MAXSERVS 8192 -int nfsd_nrthreads(struct net *net) +int nfsd_nrthreads(void) { int rv = 0; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv) - rv = nn->nfsd_serv->sv_nrthreads; + if (nfsd_serv) + rv = nfsd_serv->sv_nrthreads; mutex_unlock(&nfsd_mutex); return rv; } -static int nfsd_init_socks(struct net *net) +static int nfsd_init_socks(void) { int error; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - if (!list_empty(&nn->nfsd_serv->sv_permsocks)) + if (!list_empty(&nfsd_serv->sv_permsocks)) return 0; - error = svc_create_xprt(nn->nfsd_serv, "udp", net, PF_INET, NFS_PORT, + error = svc_create_xprt(nfsd_serv, "udp", &init_net, PF_INET, NFS_PORT, SVC_SOCK_DEFAULTS); if (error < 0) return error; - error = svc_create_xprt(nn->nfsd_serv, "tcp", net, PF_INET, NFS_PORT, + error = svc_create_xprt(nfsd_serv, "tcp", &init_net, PF_INET, NFS_PORT, SVC_SOCK_DEFAULTS); if (error < 0) return error; @@ -204,15 +202,14 @@ static int nfsd_init_socks(struct net *net) return 0; } -static int nfsd_users = 0; +static bool nfsd_up = false; -static int nfsd_startup_generic(int nrservs) +static int nfsd_startup(int nrservs) { int ret; - if (nfsd_users++) + if (nfsd_up) return 0; - /* * Readahead param cache - will no-op if it already exists. * (Note therefore results will be suboptimal if number of @@ -221,79 +218,43 @@ static int nfsd_startup_generic(int nrservs) ret = nfsd_racache_init(2*nrservs); if (ret) return ret; - ret = nfs4_state_start(); + ret = nfsd_init_socks(); if (ret) goto out_racache; - return 0; - -out_racache: - nfsd_racache_shutdown(); - return ret; -} - -static void nfsd_shutdown_generic(void) -{ - if (--nfsd_users) - return; - - nfs4_state_shutdown(); - nfsd_racache_shutdown(); -} - -static int nfsd_startup_net(int nrservs, struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - int ret; - - if (nn->nfsd_net_up) - return 0; - - ret = nfsd_startup_generic(nrservs); - if (ret) - return ret; - ret = nfsd_init_socks(net); - if (ret) - goto out_socks; - ret = lockd_up(net); + ret = lockd_up(&init_net); if (ret) - goto out_socks; - ret = nfs4_state_start_net(net); + goto out_racache; + ret = nfs4_state_start(); if (ret) goto out_lockd; - - nn->nfsd_net_up = true; + nfsd_up = true; return 0; - out_lockd: - lockd_down(net); -out_socks: - nfsd_shutdown_generic(); + lockd_down(&init_net); +out_racache: + nfsd_racache_shutdown(); return ret; } -static void nfsd_shutdown_net(struct net *net) +static void nfsd_shutdown(void) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - nfs4_state_shutdown_net(net); - lockd_down(net); - nn->nfsd_net_up = false; - nfsd_shutdown_generic(); -} - -static void nfsd_last_thread(struct svc_serv *serv, struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - /* * write_ports can create the server without actually starting * any threads--if we get shut down before any threads are * started, then nfsd_last_thread will be run before any of this * other initialization has been done. */ - if (!nn->nfsd_net_up) + if (!nfsd_up) return; - nfsd_shutdown_net(net); + nfs4_state_shutdown(); + lockd_down(&init_net); + nfsd_racache_shutdown(); + nfsd_up = false; +} + +static void nfsd_last_thread(struct svc_serv *serv, struct net *net) +{ + nfsd_shutdown(); svc_rpcb_cleanup(serv, net); @@ -366,84 +327,69 @@ static int nfsd_get_default_max_blksize(void) return ret; } -int nfsd_create_serv(struct net *net) +int nfsd_create_serv(void) { int error; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct net *net = current->nsproxy->net_ns; WARN_ON(!mutex_is_locked(&nfsd_mutex)); - if (nn->nfsd_serv) { - svc_get(nn->nfsd_serv); + if (nfsd_serv) { + svc_get(nfsd_serv); return 0; } if (nfsd_max_blksize == 0) nfsd_max_blksize = nfsd_get_default_max_blksize(); nfsd_reset_versions(); - nn->nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, + nfsd_serv = svc_create_pooled(&nfsd_program, nfsd_max_blksize, nfsd_last_thread, nfsd, THIS_MODULE); - if (nn->nfsd_serv == NULL) + if (nfsd_serv == NULL) return -ENOMEM; - error = svc_bind(nn->nfsd_serv, net); + error = svc_bind(nfsd_serv, net); if (error < 0) { - svc_destroy(nn->nfsd_serv); + svc_destroy(nfsd_serv); return error; } set_max_drc(); - do_gettimeofday(&nn->nfssvc_boot); /* record boot time */ + do_gettimeofday(&nfssvc_boot); /* record boot time */ return 0; } -int nfsd_nrpools(struct net *net) +int nfsd_nrpools(void) { - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - - if (nn->nfsd_serv == NULL) + if (nfsd_serv == NULL) return 0; else - return nn->nfsd_serv->sv_nrpools; + return nfsd_serv->sv_nrpools; } -int nfsd_get_nrthreads(int n, int *nthreads, struct net *net) +int nfsd_get_nrthreads(int n, int *nthreads) { int i = 0; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - if (nn->nfsd_serv != NULL) { - for (i = 0; i < nn->nfsd_serv->sv_nrpools && i < n; i++) - nthreads[i] = nn->nfsd_serv->sv_pools[i].sp_nrthreads; + if (nfsd_serv != NULL) { + for (i = 0; i < nfsd_serv->sv_nrpools && i < n; i++) + nthreads[i] = nfsd_serv->sv_pools[i].sp_nrthreads; } return 0; } -void nfsd_destroy(struct net *net) -{ - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - int destroy = (nn->nfsd_serv->sv_nrthreads == 1); - - if (destroy) - svc_shutdown_net(nn->nfsd_serv, net); - svc_destroy(nn->nfsd_serv); - if (destroy) - nn->nfsd_serv = NULL; -} - -int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) +int nfsd_set_nrthreads(int n, int *nthreads) { int i = 0; int tot = 0; int err = 0; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct net *net = &init_net; WARN_ON(!mutex_is_locked(&nfsd_mutex)); - if (nn->nfsd_serv == NULL || n <= 0) + if (nfsd_serv == NULL || n <= 0) return 0; - if (n > nn->nfsd_serv->sv_nrpools) - n = nn->nfsd_serv->sv_nrpools; + if (n > nfsd_serv->sv_nrpools) + n = nfsd_serv->sv_nrpools; /* enforce a global maximum number of threads */ tot = 0; @@ -473,9 +419,9 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) nthreads[0] = 1; /* apply the new numbers */ - svc_get(nn->nfsd_serv); + svc_get(nfsd_serv); for (i = 0; i < n; i++) { - err = svc_set_num_threads(nn->nfsd_serv, &nn->nfsd_serv->sv_pools[i], + err = svc_set_num_threads(nfsd_serv, &nfsd_serv->sv_pools[i], nthreads[i]); if (err) break; @@ -490,11 +436,11 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) * this is the first time nrservs is nonzero. */ int -nfsd_svc(int nrservs, struct net *net) +nfsd_svc(int nrservs) { int error; bool nfsd_up_before; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); + struct net *net = &init_net; mutex_lock(&nfsd_mutex); dprintk("nfsd: creating service\n"); @@ -503,29 +449,29 @@ nfsd_svc(int nrservs, struct net *net) if (nrservs > NFSD_MAXSERVS) nrservs = NFSD_MAXSERVS; error = 0; - if (nrservs == 0 && nn->nfsd_serv == NULL) + if (nrservs == 0 && nfsd_serv == NULL) goto out; - error = nfsd_create_serv(net); + error = nfsd_create_serv(); if (error) goto out; - nfsd_up_before = nn->nfsd_net_up; + nfsd_up_before = nfsd_up; - error = nfsd_startup_net(nrservs, net); + error = nfsd_startup(nrservs); if (error) goto out_destroy; - error = svc_set_num_threads(nn->nfsd_serv, NULL, nrservs); + error = svc_set_num_threads(nfsd_serv, NULL, nrservs); if (error) goto out_shutdown; - /* We are holding a reference to nn->nfsd_serv which + /* We are holding a reference to nfsd_serv which * we don't want to count in the return value, * so subtract 1 */ - error = nn->nfsd_serv->sv_nrthreads - 1; + error = nfsd_serv->sv_nrthreads - 1; out_shutdown: if (error < 0 && !nfsd_up_before) - nfsd_shutdown_net(net); + nfsd_shutdown(); out_destroy: nfsd_destroy(net); /* Release server */ out: @@ -541,8 +487,6 @@ static int nfsd(void *vrqstp) { struct svc_rqst *rqstp = (struct svc_rqst *) vrqstp; - struct svc_xprt *perm_sock = list_entry(rqstp->rq_server->sv_permsocks.next, typeof(struct svc_xprt), xpt_list); - struct net *net = perm_sock->xpt_net; int err; /* Lock module and set up kernel thread */ @@ -607,7 +551,7 @@ nfsd(void *vrqstp) /* Release the thread */ svc_exit_thread(rqstp); - nfsd_destroy(net); + nfsd_destroy(&init_net); /* Release module */ mutex_unlock(&nfsd_mutex); @@ -696,24 +640,21 @@ nfsd_dispatch(struct svc_rqst *rqstp, __be32 *statp) } /* Store reply in cache. */ - nfsd_cache_update(rqstp, rqstp->rq_cachetype, statp + 1); + nfsd_cache_update(rqstp, proc->pc_cachetype, statp + 1); return 1; } int nfsd_pool_stats_open(struct inode *inode, struct file *file) { int ret; - struct net *net = &init_net; - struct nfsd_net *nn = net_generic(net, nfsd_net_id); - mutex_lock(&nfsd_mutex); - if (nn->nfsd_serv == NULL) { + if (nfsd_serv == NULL) { mutex_unlock(&nfsd_mutex); return -ENODEV; } /* bump up the psudo refcount while traversing */ - svc_get(nn->nfsd_serv); - ret = svc_pool_stats_open(nn->nfsd_serv, file); + svc_get(nfsd_serv); + ret = svc_pool_stats_open(nfsd_serv, file); mutex_unlock(&nfsd_mutex); return ret; } diff --git a/trunk/fs/nfsd/nfsxdr.c b/trunk/fs/nfsd/nfsxdr.c index 979b42106979..65ec595e2226 100644 --- a/trunk/fs/nfsd/nfsxdr.c +++ b/trunk/fs/nfsd/nfsxdr.c @@ -246,7 +246,7 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readargs *args) { unsigned int len; - int v; + int v,pn; if (!(p = decode_fh(p, &args->fh))) return 0; @@ -262,9 +262,8 @@ nfssvc_decode_readargs(struct svc_rqst *rqstp, __be32 *p, */ v=0; while (len > 0) { - struct page *p = *(rqstp->rq_next_page++); - - rqstp->rq_vec[v].iov_base = page_address(p); + pn = rqstp->rq_resused++; + rqstp->rq_vec[v].iov_base = page_address(rqstp->rq_respages[pn]); rqstp->rq_vec[v].iov_len = len < PAGE_SIZE?len:PAGE_SIZE; len -= rqstp->rq_vec[v].iov_len; v++; @@ -356,7 +355,7 @@ nfssvc_decode_readlinkargs(struct svc_rqst *rqstp, __be32 *p, struct nfsd_readli { if (!(p = decode_fh(p, &args->fh))) return 0; - args->buffer = page_address(*(rqstp->rq_next_page++)); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } @@ -397,7 +396,7 @@ nfssvc_decode_readdirargs(struct svc_rqst *rqstp, __be32 *p, if (args->count > PAGE_SIZE) args->count = PAGE_SIZE; - args->buffer = page_address(*(rqstp->rq_next_page++)); + args->buffer = page_address(rqstp->rq_respages[rqstp->rq_resused++]); return xdr_argsize_check(rqstp, p); } diff --git a/trunk/fs/nfsd/state.h b/trunk/fs/nfsd/state.h index d1c229feed52..e036894bce57 100644 --- a/trunk/fs/nfsd/state.h +++ b/trunk/fs/nfsd/state.h @@ -150,12 +150,6 @@ struct nfsd4_channel_attrs { u32 rdma_attrs; }; -struct nfsd4_cb_sec { - u32 flavor; /* (u32)(-1) used to mean "no valid flavor" */ - u32 uid; - u32 gid; -}; - struct nfsd4_create_session { clientid_t clientid; struct nfs4_sessionid sessionid; @@ -164,12 +158,8 @@ struct nfsd4_create_session { struct nfsd4_channel_attrs fore_channel; struct nfsd4_channel_attrs back_channel; u32 callback_prog; - struct nfsd4_cb_sec cb_sec; -}; - -struct nfsd4_backchannel_ctl { - u32 bc_cb_program; - struct nfsd4_cb_sec bc_cb_sec; + u32 uid; + u32 gid; }; struct nfsd4_bind_conn_to_session { @@ -202,7 +192,6 @@ struct nfsd4_session { struct nfs4_sessionid se_sessionid; struct nfsd4_channel_attrs se_fchannel; struct nfsd4_channel_attrs se_bchannel; - struct nfsd4_cb_sec se_cb_sec; struct list_head se_conns; u32 se_cb_prog; u32 se_cb_seq_nr; @@ -232,12 +221,13 @@ struct nfsd4_sessionid { */ struct nfs4_client { struct list_head cl_idhash; /* hash by cl_clientid.id */ - struct rb_node cl_namenode; /* link into by-name trees */ + struct list_head cl_strhash; /* hash by cl_name */ struct list_head cl_openowners; struct idr cl_stateids; /* stateid lookup */ struct list_head cl_delegations; struct list_head cl_lru; /* tail queue */ struct xdr_netobj cl_name; /* id generated by client */ + char cl_recdir[HEXDIR_LEN]; /* recovery dir */ nfs4_verifier cl_verifier; /* generated by client */ time_t cl_time; /* time of last lease renewal */ struct sockaddr_storage cl_addr; /* client ipaddress */ @@ -252,11 +242,9 @@ struct nfs4_client { #define NFSD4_CLIENT_CB_KILL (1) #define NFSD4_CLIENT_STABLE (2) /* client on stable storage */ #define NFSD4_CLIENT_RECLAIM_COMPLETE (3) /* reclaim_complete done */ -#define NFSD4_CLIENT_CONFIRMED (4) /* client is confirmed */ #define NFSD4_CLIENT_CB_FLAG_MASK (1 << NFSD4_CLIENT_CB_UPDATE | \ 1 << NFSD4_CLIENT_CB_KILL) unsigned long cl_flags; - struct rpc_cred *cl_cb_cred; struct rpc_clnt *cl_cb_client; u32 cl_cb_ident; #define NFSD4_CB_UP 0 @@ -283,7 +271,6 @@ struct nfs4_client { unsigned long cl_cb_slot_busy; struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ /* wait here for slots */ - struct net *net; }; static inline void @@ -305,7 +292,6 @@ is_client_expired(struct nfs4_client *clp) */ struct nfs4_client_reclaim { struct list_head cr_strhash; /* hash by cr_name */ - struct nfs4_client *cr_clp; /* pointer to associated clp */ char cr_recdir[HEXDIR_LEN]; /* recover dir */ }; @@ -466,26 +452,25 @@ extern __be32 nfs4_preprocess_stateid_op(struct net *net, stateid_t *stateid, int flags, struct file **filp); extern void nfs4_lock_state(void); extern void nfs4_unlock_state(void); -void nfs4_remove_reclaim_record(struct nfs4_client_reclaim *, struct nfsd_net *); -extern void nfs4_release_reclaim(struct nfsd_net *); -extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(const char *recdir, - struct nfsd_net *nn); -extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions, struct nfsd_net *nn); +extern int nfs4_in_grace(void); +extern void nfs4_release_reclaim(void); +extern struct nfs4_client_reclaim *nfsd4_find_reclaim_client(struct nfs4_client *crp); +extern __be32 nfs4_check_open_reclaim(clientid_t *clid, bool sessions); extern void nfs4_free_openowner(struct nfs4_openowner *); extern void nfs4_free_lockowner(struct nfs4_lockowner *); extern int set_callback_cred(void); -extern void nfsd4_init_callback(struct nfsd4_callback *); extern void nfsd4_probe_callback(struct nfs4_client *clp); extern void nfsd4_probe_callback_sync(struct nfs4_client *clp); extern void nfsd4_change_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); +extern void nfsd4_do_callback_rpc(struct work_struct *); extern void nfsd4_cb_recall(struct nfs4_delegation *dp); extern int nfsd4_create_callback_queue(void); extern void nfsd4_destroy_callback_queue(void); extern void nfsd4_shutdown_callback(struct nfs4_client *); extern void nfs4_put_delegation(struct nfs4_delegation *dp); -extern struct nfs4_client_reclaim *nfs4_client_to_reclaim(const char *name, - struct nfsd_net *nn); -extern bool nfs4_has_reclaimed_state(const char *name, struct nfsd_net *nn); +extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); +extern int nfs4_client_to_reclaim(const char *name); +extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); extern void release_session_client(struct nfsd4_session *); extern void nfsd4_purge_closed_stateid(struct nfs4_stateowner *); @@ -495,28 +480,5 @@ extern void nfsd4_client_tracking_exit(struct net *net); extern void nfsd4_client_record_create(struct nfs4_client *clp); extern void nfsd4_client_record_remove(struct nfs4_client *clp); extern int nfsd4_client_record_check(struct nfs4_client *clp); -extern void nfsd4_record_grace_done(struct nfsd_net *nn, time_t boot_time); - -/* nfs fault injection functions */ -#ifdef CONFIG_NFSD_FAULT_INJECTION -int nfsd_fault_inject_init(void); -void nfsd_fault_inject_cleanup(void); -u64 nfsd_for_n_state(u64, u64 (*)(struct nfs4_client *, u64)); -struct nfs4_client *nfsd_find_client(struct sockaddr_storage *, size_t); - -u64 nfsd_forget_client(struct nfs4_client *, u64); -u64 nfsd_forget_client_locks(struct nfs4_client*, u64); -u64 nfsd_forget_client_openowners(struct nfs4_client *, u64); -u64 nfsd_forget_client_delegations(struct nfs4_client *, u64); -u64 nfsd_recall_client_delegations(struct nfs4_client *, u64); - -u64 nfsd_print_client(struct nfs4_client *, u64); -u64 nfsd_print_client_locks(struct nfs4_client *, u64); -u64 nfsd_print_client_openowners(struct nfs4_client *, u64); -u64 nfsd_print_client_delegations(struct nfs4_client *, u64); -#else /* CONFIG_NFSD_FAULT_INJECTION */ -static inline int nfsd_fault_inject_init(void) { return 0; } -static inline void nfsd_fault_inject_cleanup(void) {} -#endif /* CONFIG_NFSD_FAULT_INJECTION */ - +extern void nfsd4_record_grace_done(struct net *net, time_t boot_time); #endif /* NFSD4_STATE_H */ diff --git a/trunk/fs/nfsd/vfs.c b/trunk/fs/nfsd/vfs.c index f0a6d88d7fff..c120b48ec305 100644 --- a/trunk/fs/nfsd/vfs.c +++ b/trunk/fs/nfsd/vfs.c @@ -886,7 +886,7 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, struct splice_desc *sd) { struct svc_rqst *rqstp = sd->u.data; - struct page **pp = rqstp->rq_next_page; + struct page **pp = rqstp->rq_respages + rqstp->rq_resused; struct page *page = buf->page; size_t size; @@ -894,15 +894,17 @@ nfsd_splice_actor(struct pipe_inode_info *pipe, struct pipe_buffer *buf, if (rqstp->rq_res.page_len == 0) { get_page(page); - put_page(*rqstp->rq_next_page); - *(rqstp->rq_next_page++) = page; + put_page(*pp); + *pp = page; + rqstp->rq_resused++; rqstp->rq_res.page_base = buf->offset; rqstp->rq_res.page_len = size; } else if (page != pp[-1]) { get_page(page); - if (*rqstp->rq_next_page) - put_page(*rqstp->rq_next_page); - *(rqstp->rq_next_page++) = page; + if (*pp) + put_page(*pp); + *pp = page; + rqstp->rq_resused++; rqstp->rq_res.page_len += size; } else rqstp->rq_res.page_len += size; @@ -934,8 +936,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, .u.data = rqstp, }; - WARN_ON_ONCE(rqstp->rq_next_page != rqstp->rq_respages + 1); - rqstp->rq_next_page = rqstp->rq_respages + 1; + rqstp->rq_resused = 1; host_err = splice_direct_to_actor(file, &sd, nfsd_direct_splice_actor); } else { oldfs = get_fs(); @@ -1019,10 +1020,28 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, inode = dentry->d_inode; exp = fhp->fh_export; + /* + * Request sync writes if + * - the sync export option has been set, or + * - the client requested O_SYNC behavior (NFSv3 feature). + * - The file system doesn't support fsync(). + * When NFSv2 gathered writes have been configured for this volume, + * flushing the data to disk is handled separately below. + */ use_wgather = (rqstp->rq_vers == 2) && EX_WGATHER(exp); + if (!file->f_op->fsync) {/* COMMIT3 cannot work */ + stable = 2; + *stablep = 2; /* FILE_SYNC */ + } + if (!EX_ISSYNC(exp)) stable = 0; + if (stable && !use_wgather) { + spin_lock(&file->f_lock); + file->f_flags |= O_SYNC; + spin_unlock(&file->f_lock); + } /* Write the data. */ oldfs = get_fs(); set_fs(KERNEL_DS); @@ -1038,12 +1057,8 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, if (inode->i_mode & (S_ISUID | S_ISGID)) kill_suid(dentry); - if (stable) { - if (use_wgather) - host_err = wait_for_concurrent_writes(file); - else - host_err = vfs_fsync_range(file, offset, offset+*cnt, 0); - } + if (stable && use_wgather) + host_err = wait_for_concurrent_writes(file); out_nfserr: dprintk("nfsd: write complete host_err=%d\n", host_err); @@ -1470,19 +1485,13 @@ do_nfsd_create(struct svc_rqst *rqstp, struct svc_fh *fhp, case NFS3_CREATE_EXCLUSIVE: if ( dchild->d_inode->i_mtime.tv_sec == v_mtime && dchild->d_inode->i_atime.tv_sec == v_atime - && dchild->d_inode->i_size == 0 ) { - if (created) - *created = 1; + && dchild->d_inode->i_size == 0 ) break; - } case NFS4_CREATE_EXCLUSIVE4_1: if ( dchild->d_inode->i_mtime.tv_sec == v_mtime && dchild->d_inode->i_atime.tv_sec == v_atime - && dchild->d_inode->i_size == 0 ) { - if (created) - *created = 1; + && dchild->d_inode->i_size == 0 ) goto set_attr; - } /* fallthru */ case NFS3_CREATE_GUARDED: err = nfserr_exist; diff --git a/trunk/fs/nfsd/xdr4.h b/trunk/fs/nfsd/xdr4.h index 0889bfb43dc9..acd127d4ee82 100644 --- a/trunk/fs/nfsd/xdr4.h +++ b/trunk/fs/nfsd/xdr4.h @@ -385,8 +385,7 @@ struct nfsd4_write { u64 wr_offset; /* request */ u32 wr_stable_how; /* request */ u32 wr_buflen; /* request */ - struct kvec wr_head; - struct page ** wr_pagelist; /* request */ + int wr_vlen; u32 wr_bytes_written; /* response */ u32 wr_how_written; /* response */ @@ -463,7 +462,6 @@ struct nfsd4_op { /* NFSv4.1 */ struct nfsd4_exchange_id exchange_id; - struct nfsd4_backchannel_ctl backchannel_ctl; struct nfsd4_bind_conn_to_session bind_conn_to_session; struct nfsd4_create_session create_session; struct nfsd4_destroy_session destroy_session; @@ -528,14 +526,6 @@ static inline bool nfsd4_not_cached(struct nfsd4_compoundres *resp) || nfsd4_is_solo_sequence(resp); } -static inline bool nfsd4_last_compound_op(struct svc_rqst *rqstp) -{ - struct nfsd4_compoundres *resp = rqstp->rq_resp; - struct nfsd4_compoundargs *argp = rqstp->rq_argp; - - return argp->opcnt == resp->opcnt; -} - #define NFS4_SVC_XDRSIZE sizeof(struct nfsd4_compoundargs) static inline void @@ -576,7 +566,6 @@ extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, struct nfsd4_sequence *seq); extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, struct nfsd4_compound_state *, struct nfsd4_exchange_id *); -extern __be32 nfsd4_backchannel_ctl(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_backchannel_ctl *); extern __be32 nfsd4_bind_conn_to_session(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_bind_conn_to_session *); extern __be32 nfsd4_create_session(struct svc_rqst *, struct nfsd4_compound_state *, @@ -590,7 +579,7 @@ extern __be32 nfsd4_destroy_session(struct svc_rqst *, extern __be32 nfsd4_destroy_clientid(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_destroy_clientid *); __be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, - struct nfsd4_open *open, struct nfsd_net *nn); + struct nfsd4_open *open); extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nfsd4_open *open); extern void nfsd4_cleanup_open_state(struct nfsd4_open *open, __be32 status); diff --git a/trunk/include/linux/backing-dev.h b/trunk/include/linux/backing-dev.h index 12731a19ef06..2a9a9abc9126 100644 --- a/trunk/include/linux/backing-dev.h +++ b/trunk/include/linux/backing-dev.h @@ -114,7 +114,6 @@ struct backing_dev_info { int bdi_init(struct backing_dev_info *bdi); void bdi_destroy(struct backing_dev_info *bdi); -__printf(3, 4) int bdi_register(struct backing_dev_info *bdi, struct device *parent, const char *fmt, ...); int bdi_register_dev(struct backing_dev_info *bdi, dev_t dev); diff --git a/trunk/include/linux/ceph/libceph.h b/trunk/include/linux/ceph/libceph.h index 084d3c622b12..6470792b13d3 100644 --- a/trunk/include/linux/ceph/libceph.h +++ b/trunk/include/linux/ceph/libceph.h @@ -43,6 +43,7 @@ struct ceph_options { struct ceph_entity_addr my_addr; int mount_timeout; int osd_idle_ttl; + int osd_timeout; int osd_keepalive_timeout; /* @@ -62,6 +63,7 @@ struct ceph_options { * defaults */ #define CEPH_MOUNT_TIMEOUT_DEFAULT 60 +#define CEPH_OSD_TIMEOUT_DEFAULT 60 /* seconds */ #define CEPH_OSD_KEEPALIVE_DEFAULT 5 #define CEPH_OSD_IDLE_TTL_DEFAULT 60 diff --git a/trunk/include/linux/ceph/osdmap.h b/trunk/include/linux/ceph/osdmap.h index 10a417f9f76f..e37acbe989a9 100644 --- a/trunk/include/linux/ceph/osdmap.h +++ b/trunk/include/linux/ceph/osdmap.h @@ -123,7 +123,6 @@ extern int ceph_calc_pg_acting(struct ceph_osdmap *osdmap, struct ceph_pg pgid, extern int ceph_calc_pg_primary(struct ceph_osdmap *osdmap, struct ceph_pg pgid); -extern const char *ceph_pg_pool_name_by_id(struct ceph_osdmap *map, u64 id); extern int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name); #endif diff --git a/trunk/include/linux/ceph/rados.h b/trunk/include/linux/ceph/rados.h index 2c04afeead1c..de91fbdf127e 100644 --- a/trunk/include/linux/ceph/rados.h +++ b/trunk/include/linux/ceph/rados.h @@ -87,8 +87,6 @@ struct ceph_pg { * * lpgp_num -- as above. */ -#define CEPH_NOPOOL ((__u64) (-1)) /* pool id not defined */ - #define CEPH_PG_TYPE_REP 1 #define CEPH_PG_TYPE_RAID4 2 #define CEPH_PG_POOL_VERSION 2 diff --git a/trunk/include/linux/dma-debug.h b/trunk/include/linux/dma-debug.h index fc0e34ce038f..171ad8aedc83 100644 --- a/trunk/include/linux/dma-debug.h +++ b/trunk/include/linux/dma-debug.h @@ -39,8 +39,6 @@ extern void debug_dma_map_page(struct device *dev, struct page *page, int direction, dma_addr_t dma_addr, bool map_single); -extern void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr); - extern void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single); @@ -107,11 +105,6 @@ static inline void debug_dma_map_page(struct device *dev, struct page *page, { } -static inline void debug_dma_mapping_error(struct device *dev, - dma_addr_t dma_addr) -{ -} - static inline void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single) diff --git a/trunk/include/linux/exportfs.h b/trunk/include/linux/exportfs.h index 5b9b5b317180..c7e6b6392ab8 100644 --- a/trunk/include/linux/exportfs.h +++ b/trunk/include/linux/exportfs.h @@ -83,11 +83,6 @@ enum fid_type { * 64 bit parent inode number. */ FILEID_NILFS_WITH_PARENT = 0x62, - - /* - * Filesystems must not use 0xff file ID. - */ - FILEID_INVALID = 0xff, }; struct fid { diff --git a/trunk/include/linux/f2fs_fs.h b/trunk/include/linux/f2fs_fs.h deleted file mode 100644 index f9a12f6243a5..000000000000 --- a/trunk/include/linux/f2fs_fs.h +++ /dev/null @@ -1,413 +0,0 @@ -/** - * include/linux/f2fs_fs.h - * - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#ifndef _LINUX_F2FS_FS_H -#define _LINUX_F2FS_FS_H - -#include -#include - -#define F2FS_SUPER_OFFSET 1024 /* byte-size offset */ -#define F2FS_LOG_SECTOR_SIZE 9 /* 9 bits for 512 byte */ -#define F2FS_LOG_SECTORS_PER_BLOCK 3 /* 4KB: F2FS_BLKSIZE */ -#define F2FS_BLKSIZE 4096 /* support only 4KB block */ -#define F2FS_MAX_EXTENSION 64 /* # of extension entries */ - -#define NULL_ADDR 0x0U -#define NEW_ADDR -1U - -#define F2FS_ROOT_INO(sbi) (sbi->root_ino_num) -#define F2FS_NODE_INO(sbi) (sbi->node_ino_num) -#define F2FS_META_INO(sbi) (sbi->meta_ino_num) - -/* This flag is used by node and meta inodes, and by recovery */ -#define GFP_F2FS_ZERO (GFP_NOFS | __GFP_ZERO) - -/* - * For further optimization on multi-head logs, on-disk layout supports maximum - * 16 logs by default. The number, 16, is expected to cover all the cases - * enoughly. The implementaion currently uses no more than 6 logs. - * Half the logs are used for nodes, and the other half are used for data. - */ -#define MAX_ACTIVE_LOGS 16 -#define MAX_ACTIVE_NODE_LOGS 8 -#define MAX_ACTIVE_DATA_LOGS 8 - -/* - * For superblock - */ -struct f2fs_super_block { - __le32 magic; /* Magic Number */ - __le16 major_ver; /* Major Version */ - __le16 minor_ver; /* Minor Version */ - __le32 log_sectorsize; /* log2 sector size in bytes */ - __le32 log_sectors_per_block; /* log2 # of sectors per block */ - __le32 log_blocksize; /* log2 block size in bytes */ - __le32 log_blocks_per_seg; /* log2 # of blocks per segment */ - __le32 segs_per_sec; /* # of segments per section */ - __le32 secs_per_zone; /* # of sections per zone */ - __le32 checksum_offset; /* checksum offset inside super block */ - __le64 block_count; /* total # of user blocks */ - __le32 section_count; /* total # of sections */ - __le32 segment_count; /* total # of segments */ - __le32 segment_count_ckpt; /* # of segments for checkpoint */ - __le32 segment_count_sit; /* # of segments for SIT */ - __le32 segment_count_nat; /* # of segments for NAT */ - __le32 segment_count_ssa; /* # of segments for SSA */ - __le32 segment_count_main; /* # of segments for main area */ - __le32 segment0_blkaddr; /* start block address of segment 0 */ - __le32 cp_blkaddr; /* start block address of checkpoint */ - __le32 sit_blkaddr; /* start block address of SIT */ - __le32 nat_blkaddr; /* start block address of NAT */ - __le32 ssa_blkaddr; /* start block address of SSA */ - __le32 main_blkaddr; /* start block address of main area */ - __le32 root_ino; /* root inode number */ - __le32 node_ino; /* node inode number */ - __le32 meta_ino; /* meta inode number */ - __u8 uuid[16]; /* 128-bit uuid for volume */ - __le16 volume_name[512]; /* volume name */ - __le32 extension_count; /* # of extensions below */ - __u8 extension_list[F2FS_MAX_EXTENSION][8]; /* extension array */ -} __packed; - -/* - * For checkpoint - */ -#define CP_ERROR_FLAG 0x00000008 -#define CP_COMPACT_SUM_FLAG 0x00000004 -#define CP_ORPHAN_PRESENT_FLAG 0x00000002 -#define CP_UMOUNT_FLAG 0x00000001 - -struct f2fs_checkpoint { - __le64 checkpoint_ver; /* checkpoint block version number */ - __le64 user_block_count; /* # of user blocks */ - __le64 valid_block_count; /* # of valid blocks in main area */ - __le32 rsvd_segment_count; /* # of reserved segments for gc */ - __le32 overprov_segment_count; /* # of overprovision segments */ - __le32 free_segment_count; /* # of free segments in main area */ - - /* information of current node segments */ - __le32 cur_node_segno[MAX_ACTIVE_NODE_LOGS]; - __le16 cur_node_blkoff[MAX_ACTIVE_NODE_LOGS]; - /* information of current data segments */ - __le32 cur_data_segno[MAX_ACTIVE_DATA_LOGS]; - __le16 cur_data_blkoff[MAX_ACTIVE_DATA_LOGS]; - __le32 ckpt_flags; /* Flags : umount and journal_present */ - __le32 cp_pack_total_block_count; /* total # of one cp pack */ - __le32 cp_pack_start_sum; /* start block number of data summary */ - __le32 valid_node_count; /* Total number of valid nodes */ - __le32 valid_inode_count; /* Total number of valid inodes */ - __le32 next_free_nid; /* Next free node number */ - __le32 sit_ver_bitmap_bytesize; /* Default value 64 */ - __le32 nat_ver_bitmap_bytesize; /* Default value 256 */ - __le32 checksum_offset; /* checksum offset inside cp block */ - __le64 elapsed_time; /* mounted time */ - /* allocation type of current segment */ - unsigned char alloc_type[MAX_ACTIVE_LOGS]; - - /* SIT and NAT version bitmap */ - unsigned char sit_nat_version_bitmap[1]; -} __packed; - -/* - * For orphan inode management - */ -#define F2FS_ORPHANS_PER_BLOCK 1020 - -struct f2fs_orphan_block { - __le32 ino[F2FS_ORPHANS_PER_BLOCK]; /* inode numbers */ - __le32 reserved; /* reserved */ - __le16 blk_addr; /* block index in current CP */ - __le16 blk_count; /* Number of orphan inode blocks in CP */ - __le32 entry_count; /* Total number of orphan nodes in current CP */ - __le32 check_sum; /* CRC32 for orphan inode block */ -} __packed; - -/* - * For NODE structure - */ -struct f2fs_extent { - __le32 fofs; /* start file offset of the extent */ - __le32 blk_addr; /* start block address of the extent */ - __le32 len; /* lengh of the extent */ -} __packed; - -#define F2FS_MAX_NAME_LEN 256 -#define ADDRS_PER_INODE 923 /* Address Pointers in an Inode */ -#define ADDRS_PER_BLOCK 1018 /* Address Pointers in a Direct Block */ -#define NIDS_PER_BLOCK 1018 /* Node IDs in an Indirect Block */ - -struct f2fs_inode { - __le16 i_mode; /* file mode */ - __u8 i_advise; /* file hints */ - __u8 i_reserved; /* reserved */ - __le32 i_uid; /* user ID */ - __le32 i_gid; /* group ID */ - __le32 i_links; /* links count */ - __le64 i_size; /* file size in bytes */ - __le64 i_blocks; /* file size in blocks */ - __le64 i_atime; /* access time */ - __le64 i_ctime; /* change time */ - __le64 i_mtime; /* modification time */ - __le32 i_atime_nsec; /* access time in nano scale */ - __le32 i_ctime_nsec; /* change time in nano scale */ - __le32 i_mtime_nsec; /* modification time in nano scale */ - __le32 i_generation; /* file version (for NFS) */ - __le32 i_current_depth; /* only for directory depth */ - __le32 i_xattr_nid; /* nid to save xattr */ - __le32 i_flags; /* file attributes */ - __le32 i_pino; /* parent inode number */ - __le32 i_namelen; /* file name length */ - __u8 i_name[F2FS_MAX_NAME_LEN]; /* file name for SPOR */ - - struct f2fs_extent i_ext; /* caching a largest extent */ - - __le32 i_addr[ADDRS_PER_INODE]; /* Pointers to data blocks */ - - __le32 i_nid[5]; /* direct(2), indirect(2), - double_indirect(1) node id */ -} __packed; - -struct direct_node { - __le32 addr[ADDRS_PER_BLOCK]; /* array of data block address */ -} __packed; - -struct indirect_node { - __le32 nid[NIDS_PER_BLOCK]; /* array of data block address */ -} __packed; - -enum { - COLD_BIT_SHIFT = 0, - FSYNC_BIT_SHIFT, - DENT_BIT_SHIFT, - OFFSET_BIT_SHIFT -}; - -struct node_footer { - __le32 nid; /* node id */ - __le32 ino; /* inode nunmber */ - __le32 flag; /* include cold/fsync/dentry marks and offset */ - __le64 cp_ver; /* checkpoint version */ - __le32 next_blkaddr; /* next node page block address */ -} __packed; - -struct f2fs_node { - /* can be one of three types: inode, direct, and indirect types */ - union { - struct f2fs_inode i; - struct direct_node dn; - struct indirect_node in; - }; - struct node_footer footer; -} __packed; - -/* - * For NAT entries - */ -#define NAT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_nat_entry)) - -struct f2fs_nat_entry { - __u8 version; /* latest version of cached nat entry */ - __le32 ino; /* inode number */ - __le32 block_addr; /* block address */ -} __packed; - -struct f2fs_nat_block { - struct f2fs_nat_entry entries[NAT_ENTRY_PER_BLOCK]; -} __packed; - -/* - * For SIT entries - * - * Each segment is 2MB in size by default so that a bitmap for validity of - * there-in blocks should occupy 64 bytes, 512 bits. - * Not allow to change this. - */ -#define SIT_VBLOCK_MAP_SIZE 64 -#define SIT_ENTRY_PER_BLOCK (PAGE_CACHE_SIZE / sizeof(struct f2fs_sit_entry)) - -/* - * Note that f2fs_sit_entry->vblocks has the following bit-field information. - * [15:10] : allocation type such as CURSEG_XXXX_TYPE - * [9:0] : valid block count - */ -#define SIT_VBLOCKS_SHIFT 10 -#define SIT_VBLOCKS_MASK ((1 << SIT_VBLOCKS_SHIFT) - 1) -#define GET_SIT_VBLOCKS(raw_sit) \ - (le16_to_cpu((raw_sit)->vblocks) & SIT_VBLOCKS_MASK) -#define GET_SIT_TYPE(raw_sit) \ - ((le16_to_cpu((raw_sit)->vblocks) & ~SIT_VBLOCKS_MASK) \ - >> SIT_VBLOCKS_SHIFT) - -struct f2fs_sit_entry { - __le16 vblocks; /* reference above */ - __u8 valid_map[SIT_VBLOCK_MAP_SIZE]; /* bitmap for valid blocks */ - __le64 mtime; /* segment age for cleaning */ -} __packed; - -struct f2fs_sit_block { - struct f2fs_sit_entry entries[SIT_ENTRY_PER_BLOCK]; -} __packed; - -/* - * For segment summary - * - * One summary block contains exactly 512 summary entries, which represents - * exactly 2MB segment by default. Not allow to change the basic units. - * - * NOTE: For initializing fields, you must use set_summary - * - * - If data page, nid represents dnode's nid - * - If node page, nid represents the node page's nid. - * - * The ofs_in_node is used by only data page. It represents offset - * from node's page's beginning to get a data block address. - * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node) - */ -#define ENTRIES_IN_SUM 512 -#define SUMMARY_SIZE (7) /* sizeof(struct summary) */ -#define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */ -#define SUM_ENTRY_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) - -/* a summary entry for a 4KB-sized block in a segment */ -struct f2fs_summary { - __le32 nid; /* parent node id */ - union { - __u8 reserved[3]; - struct { - __u8 version; /* node version number */ - __le16 ofs_in_node; /* block index in parent node */ - } __packed; - }; -} __packed; - -/* summary block type, node or data, is stored to the summary_footer */ -#define SUM_TYPE_NODE (1) -#define SUM_TYPE_DATA (0) - -struct summary_footer { - unsigned char entry_type; /* SUM_TYPE_XXX */ - __u32 check_sum; /* summary checksum */ -} __packed; - -#define SUM_JOURNAL_SIZE (F2FS_BLKSIZE - SUM_FOOTER_SIZE -\ - SUM_ENTRY_SIZE) -#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ - sizeof(struct nat_journal_entry)) -#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ - sizeof(struct nat_journal_entry)) -#define SIT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ - sizeof(struct sit_journal_entry)) -#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ - sizeof(struct sit_journal_entry)) -/* - * frequently updated NAT/SIT entries can be stored in the spare area in - * summary blocks - */ -enum { - NAT_JOURNAL = 0, - SIT_JOURNAL -}; - -struct nat_journal_entry { - __le32 nid; - struct f2fs_nat_entry ne; -} __packed; - -struct nat_journal { - struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; - __u8 reserved[NAT_JOURNAL_RESERVED]; -} __packed; - -struct sit_journal_entry { - __le32 segno; - struct f2fs_sit_entry se; -} __packed; - -struct sit_journal { - struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; - __u8 reserved[SIT_JOURNAL_RESERVED]; -} __packed; - -/* 4KB-sized summary block structure */ -struct f2fs_summary_block { - struct f2fs_summary entries[ENTRIES_IN_SUM]; - union { - __le16 n_nats; - __le16 n_sits; - }; - /* spare area is used by NAT or SIT journals */ - union { - struct nat_journal nat_j; - struct sit_journal sit_j; - }; - struct summary_footer footer; -} __packed; - -/* - * For directory operations - */ -#define F2FS_DOT_HASH 0 -#define F2FS_DDOT_HASH F2FS_DOT_HASH -#define F2FS_MAX_HASH (~((0x3ULL) << 62)) -#define F2FS_HASH_COL_BIT ((0x1ULL) << 63) - -typedef __le32 f2fs_hash_t; - -/* One directory entry slot covers 8bytes-long file name */ -#define F2FS_NAME_LEN 8 -#define F2FS_NAME_LEN_BITS 3 - -#define GET_DENTRY_SLOTS(x) ((x + F2FS_NAME_LEN - 1) >> F2FS_NAME_LEN_BITS) - -/* the number of dentry in a block */ -#define NR_DENTRY_IN_BLOCK 214 - -/* MAX level for dir lookup */ -#define MAX_DIR_HASH_DEPTH 63 - -#define SIZE_OF_DIR_ENTRY 11 /* by byte */ -#define SIZE_OF_DENTRY_BITMAP ((NR_DENTRY_IN_BLOCK + BITS_PER_BYTE - 1) / \ - BITS_PER_BYTE) -#define SIZE_OF_RESERVED (PAGE_SIZE - ((SIZE_OF_DIR_ENTRY + \ - F2FS_NAME_LEN) * \ - NR_DENTRY_IN_BLOCK + SIZE_OF_DENTRY_BITMAP)) - -/* One directory entry slot representing F2FS_NAME_LEN-sized file name */ -struct f2fs_dir_entry { - __le32 hash_code; /* hash code of file name */ - __le32 ino; /* inode number */ - __le16 name_len; /* lengh of file name */ - __u8 file_type; /* file type */ -} __packed; - -/* 4KB-sized directory entry block */ -struct f2fs_dentry_block { - /* validity bitmap for directory entries in each block */ - __u8 dentry_bitmap[SIZE_OF_DENTRY_BITMAP]; - __u8 reserved[SIZE_OF_RESERVED]; - struct f2fs_dir_entry dentry[NR_DENTRY_IN_BLOCK]; - __u8 filename[NR_DENTRY_IN_BLOCK][F2FS_NAME_LEN]; -} __packed; - -/* file types used in inode_info->flags */ -enum { - F2FS_FT_UNKNOWN, - F2FS_FT_REG_FILE, - F2FS_FT_DIR, - F2FS_FT_CHRDEV, - F2FS_FT_BLKDEV, - F2FS_FT_FIFO, - F2FS_FT_SOCK, - F2FS_FT_SYMLINK, - F2FS_FT_MAX -}; - -#endif /* _LINUX_F2FS_FS_H */ diff --git a/trunk/include/linux/platform_data/iommu-omap.h b/trunk/include/linux/platform_data/iommu-omap.h index 5b429c43a297..c677b9f2fefa 100644 --- a/trunk/include/linux/platform_data/iommu-omap.h +++ b/trunk/include/linux/platform_data/iommu-omap.h @@ -10,8 +10,6 @@ * published by the Free Software Foundation. */ -#include - #define MMU_REG_SIZE 256 /** @@ -44,11 +42,8 @@ struct omap_mmu_dev_attr { struct iommu_platform_data { const char *name; - const char *reset_name; - int nr_tlb_entries; + const char *clk_name; + const int nr_tlb_entries; u32 da_start; u32 da_end; - - int (*assert_reset)(struct platform_device *pdev, const char *name); - int (*deassert_reset)(struct platform_device *pdev, const char *name); }; diff --git a/trunk/include/linux/sunrpc/cache.h b/trunk/include/linux/sunrpc/cache.h index 5dc9ee4d616e..f792794f6634 100644 --- a/trunk/include/linux/sunrpc/cache.h +++ b/trunk/include/linux/sunrpc/cache.h @@ -217,8 +217,6 @@ extern int qword_get(char **bpp, char *dest, int bufsize); static inline int get_int(char **bpp, int *anint) { char buf[50]; - char *ep; - int rv; int len = qword_get(bpp, buf, sizeof(buf)); if (len < 0) @@ -226,11 +224,9 @@ static inline int get_int(char **bpp, int *anint) if (len == 0) return -ENOENT; - rv = simple_strtol(buf, &ep, 0); - if (*ep) + if (kstrtoint(buf, 0, anint)) return -EINVAL; - *anint = rv; return 0; } diff --git a/trunk/include/linux/sunrpc/svc.h b/trunk/include/linux/sunrpc/svc.h index 676ddf53b3ee..d83db800fe02 100644 --- a/trunk/include/linux/sunrpc/svc.h +++ b/trunk/include/linux/sunrpc/svc.h @@ -243,7 +243,6 @@ struct svc_rqst { struct page * rq_pages[RPCSVC_MAXPAGES]; struct page * *rq_respages; /* points into rq_pages */ int rq_resused; /* number of pages used for result */ - struct page * *rq_next_page; /* next reply page to use */ struct kvec rq_vec[RPCSVC_MAXPAGES]; /* generally useful.. */ @@ -339,8 +338,9 @@ xdr_ressize_check(struct svc_rqst *rqstp, __be32 *p) static inline void svc_free_res_pages(struct svc_rqst *rqstp) { - while (rqstp->rq_next_page != rqstp->rq_respages) { - struct page **pp = --rqstp->rq_next_page; + while (rqstp->rq_resused) { + struct page **pp = (rqstp->rq_respages + + --rqstp->rq_resused); if (*pp) { put_page(*pp); *pp = NULL; diff --git a/trunk/include/linux/sunrpc/svcsock.h b/trunk/include/linux/sunrpc/svcsock.h index 62fd1b756e99..92ad02f0dcc0 100644 --- a/trunk/include/linux/sunrpc/svcsock.h +++ b/trunk/include/linux/sunrpc/svcsock.h @@ -26,28 +26,11 @@ struct svc_sock { void (*sk_owspace)(struct sock *); /* private TCP part */ - /* On-the-wire fragment header: */ - __be32 sk_reclen; - /* As we receive a record, this includes the length received so - * far (including the fragment header): */ - u32 sk_tcplen; - /* Total length of the data (not including fragment headers) - * received so far in the fragments making up this rpc: */ - u32 sk_datalen; - + u32 sk_reclen; /* length of record */ + u32 sk_tcplen; /* current read length */ struct page * sk_pages[RPCSVC_MAXPAGES]; /* received data */ }; -static inline u32 svc_sock_reclen(struct svc_sock *svsk) -{ - return ntohl(svsk->sk_reclen) & RPC_FRAGMENT_SIZE_MASK; -} - -static inline u32 svc_sock_final_rec(struct svc_sock *svsk) -{ - return ntohl(svsk->sk_reclen) & RPC_LAST_STREAM_FRAGMENT; -} - /* * Function prototypes. */ diff --git a/trunk/include/uapi/linux/magic.h b/trunk/include/uapi/linux/magic.h index 873e086ce3a1..12f68c7ceba6 100644 --- a/trunk/include/uapi/linux/magic.h +++ b/trunk/include/uapi/linux/magic.h @@ -23,7 +23,6 @@ #define EXT4_SUPER_MAGIC 0xEF53 #define BTRFS_SUPER_MAGIC 0x9123683E #define NILFS_SUPER_MAGIC 0x3434 -#define F2FS_SUPER_MAGIC 0xF2F52010 #define HPFS_SUPER_MAGIC 0xf995e849 #define ISOFS_SUPER_MAGIC 0x9660 #define JFFS2_SUPER_MAGIC 0x72b6 diff --git a/trunk/lib/atomic64.c b/trunk/lib/atomic64.c index 08a4f068e61e..978537809d84 100644 --- a/trunk/lib/atomic64.c +++ b/trunk/lib/atomic64.c @@ -31,11 +31,7 @@ static union { raw_spinlock_t lock; char pad[L1_CACHE_BYTES]; -} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp = { - [0 ... (NR_LOCKS - 1)] = { - .lock = __RAW_SPIN_LOCK_UNLOCKED(atomic64_lock.lock), - }, -}; +} atomic64_lock[NR_LOCKS] __cacheline_aligned_in_smp; static inline raw_spinlock_t *lock_addr(const atomic64_t *v) { @@ -177,3 +173,14 @@ int atomic64_add_unless(atomic64_t *v, long long a, long long u) return ret; } EXPORT_SYMBOL(atomic64_add_unless); + +static int init_atomic64_lock(void) +{ + int i; + + for (i = 0; i < NR_LOCKS; ++i) + raw_spin_lock_init(&atomic64_lock[i].lock); + return 0; +} + +pure_initcall(init_atomic64_lock); diff --git a/trunk/lib/dma-debug.c b/trunk/lib/dma-debug.c index 5e396accd3d0..d84beb994f36 100644 --- a/trunk/lib/dma-debug.c +++ b/trunk/lib/dma-debug.c @@ -45,12 +45,6 @@ enum { dma_debug_coherent, }; -enum map_err_types { - MAP_ERR_CHECK_NOT_APPLICABLE, - MAP_ERR_NOT_CHECKED, - MAP_ERR_CHECKED, -}; - #define DMA_DEBUG_STACKTRACE_ENTRIES 5 struct dma_debug_entry { @@ -63,7 +57,6 @@ struct dma_debug_entry { int direction; int sg_call_ents; int sg_mapped_ents; - enum map_err_types map_err_type; #ifdef CONFIG_STACKTRACE struct stack_trace stacktrace; unsigned long st_entries[DMA_DEBUG_STACKTRACE_ENTRIES]; @@ -121,12 +114,6 @@ static struct device_driver *current_driver __read_mostly; static DEFINE_RWLOCK(driver_name_lock); -static const char *const maperr2str[] = { - [MAP_ERR_CHECK_NOT_APPLICABLE] = "dma map error check not applicable", - [MAP_ERR_NOT_CHECKED] = "dma map error not checked", - [MAP_ERR_CHECKED] = "dma map error checked", -}; - static const char *type2name[4] = { "single", "page", "scather-gather", "coherent" }; @@ -389,12 +376,11 @@ void debug_dma_dump_mappings(struct device *dev) list_for_each_entry(entry, &bucket->list, list) { if (!dev || dev == entry->dev) { dev_info(entry->dev, - "%s idx %d P=%Lx D=%Lx L=%Lx %s %s\n", + "%s idx %d P=%Lx D=%Lx L=%Lx %s\n", type2name[entry->type], idx, (unsigned long long)entry->paddr, entry->dev_addr, entry->size, - dir2name[entry->direction], - maperr2str[entry->map_err_type]); + dir2name[entry->direction]); } } @@ -858,16 +844,16 @@ static void check_unmap(struct dma_debug_entry *ref) struct hash_bucket *bucket; unsigned long flags; + if (dma_mapping_error(ref->dev, ref->dev_addr)) { + err_printk(ref->dev, NULL, "DMA-API: device driver tries " + "to free an invalid DMA memory address\n"); + return; + } + bucket = get_hash_bucket(ref, &flags); entry = bucket_find_exact(bucket, ref); if (!entry) { - if (dma_mapping_error(ref->dev, ref->dev_addr)) { - err_printk(ref->dev, NULL, - "DMA-API: device driver tries " - "to free an invalid DMA memory address\n"); - return; - } err_printk(ref->dev, NULL, "DMA-API: device driver tries " "to free DMA memory it has not allocated " "[device address=0x%016llx] [size=%llu bytes]\n", @@ -924,15 +910,6 @@ static void check_unmap(struct dma_debug_entry *ref) dir2name[ref->direction]); } - if (entry->map_err_type == MAP_ERR_NOT_CHECKED) { - err_printk(ref->dev, entry, - "DMA-API: device driver failed to check map error" - "[device address=0x%016llx] [size=%llu bytes] " - "[mapped as %s]", - ref->dev_addr, ref->size, - type2name[entry->type]); - } - hash_bucket_del(entry); dma_entry_free(entry); @@ -1040,7 +1017,7 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, if (unlikely(global_disable)) return; - if (dma_mapping_error(dev, dma_addr)) + if (unlikely(dma_mapping_error(dev, dma_addr))) return; entry = dma_entry_alloc(); @@ -1053,7 +1030,6 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, entry->dev_addr = dma_addr; entry->size = size; entry->direction = direction; - entry->map_err_type = MAP_ERR_NOT_CHECKED; if (map_single) entry->type = dma_debug_single; @@ -1069,30 +1045,6 @@ void debug_dma_map_page(struct device *dev, struct page *page, size_t offset, } EXPORT_SYMBOL(debug_dma_map_page); -void debug_dma_mapping_error(struct device *dev, dma_addr_t dma_addr) -{ - struct dma_debug_entry ref; - struct dma_debug_entry *entry; - struct hash_bucket *bucket; - unsigned long flags; - - if (unlikely(global_disable)) - return; - - ref.dev = dev; - ref.dev_addr = dma_addr; - bucket = get_hash_bucket(&ref, &flags); - entry = bucket_find_exact(bucket, &ref); - - if (!entry) - goto out; - - entry->map_err_type = MAP_ERR_CHECKED; -out: - put_hash_bucket(bucket, &flags); -} -EXPORT_SYMBOL(debug_dma_mapping_error); - void debug_dma_unmap_page(struct device *dev, dma_addr_t addr, size_t size, int direction, bool map_single) { diff --git a/trunk/net/ceph/ceph_common.c b/trunk/net/ceph/ceph_common.c index ee71ea26777a..a8020293f342 100644 --- a/trunk/net/ceph/ceph_common.c +++ b/trunk/net/ceph/ceph_common.c @@ -305,6 +305,7 @@ ceph_parse_options(char *options, const char *dev_name, /* start with defaults */ opt->flags = CEPH_OPT_DEFAULT; + opt->osd_timeout = CEPH_OSD_TIMEOUT_DEFAULT; opt->osd_keepalive_timeout = CEPH_OSD_KEEPALIVE_DEFAULT; opt->mount_timeout = CEPH_MOUNT_TIMEOUT_DEFAULT; /* seconds */ opt->osd_idle_ttl = CEPH_OSD_IDLE_TTL_DEFAULT; /* seconds */ @@ -390,7 +391,7 @@ ceph_parse_options(char *options, const char *dev_name, /* misc */ case Opt_osdtimeout: - pr_warning("ignoring deprecated osdtimeout option\n"); + opt->osd_timeout = intval; break; case Opt_osdkeepalivetimeout: opt->osd_keepalive_timeout = intval; diff --git a/trunk/net/ceph/messenger.c b/trunk/net/ceph/messenger.c index 4d111fd2b492..3ef1759403b4 100644 --- a/trunk/net/ceph/messenger.c +++ b/trunk/net/ceph/messenger.c @@ -2244,62 +2244,22 @@ static int try_read(struct ceph_connection *con) /* - * Atomically queue work on a connection after the specified delay. - * Bump @con reference to avoid races with connection teardown. - * Returns 0 if work was queued, or an error code otherwise. + * Atomically queue work on a connection. Bump @con reference to + * avoid races with connection teardown. */ -static int queue_con_delay(struct ceph_connection *con, unsigned long delay) +static void queue_con(struct ceph_connection *con) { if (!con->ops->get(con)) { - dout("%s %p ref count 0\n", __func__, con); - - return -ENOENT; + dout("queue_con %p ref count 0\n", con); + return; } - if (!queue_delayed_work(ceph_msgr_wq, &con->work, delay)) { - dout("%s %p - already queued\n", __func__, con); + if (!queue_delayed_work(ceph_msgr_wq, &con->work, 0)) { + dout("queue_con %p - already queued\n", con); con->ops->put(con); - - return -EBUSY; - } - - dout("%s %p %lu\n", __func__, con, delay); - - return 0; -} - -static void queue_con(struct ceph_connection *con) -{ - (void) queue_con_delay(con, 0); -} - -static bool con_sock_closed(struct ceph_connection *con) -{ - if (!test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) - return false; - -#define CASE(x) \ - case CON_STATE_ ## x: \ - con->error_msg = "socket closed (con state " #x ")"; \ - break; - - switch (con->state) { - CASE(CLOSED); - CASE(PREOPEN); - CASE(CONNECTING); - CASE(NEGOTIATING); - CASE(OPEN); - CASE(STANDBY); - default: - pr_warning("%s con %p unrecognized state %lu\n", - __func__, con, con->state); - con->error_msg = "unrecognized con state"; - BUG(); - break; + } else { + dout("queue_con %p\n", con); } -#undef CASE - - return true; } /* @@ -2313,16 +2273,35 @@ static void con_work(struct work_struct *work) mutex_lock(&con->mutex); restart: - if (con_sock_closed(con)) + if (test_and_clear_bit(CON_FLAG_SOCK_CLOSED, &con->flags)) { + switch (con->state) { + case CON_STATE_CONNECTING: + con->error_msg = "connection failed"; + break; + case CON_STATE_NEGOTIATING: + con->error_msg = "negotiation failed"; + break; + case CON_STATE_OPEN: + con->error_msg = "socket closed"; + break; + default: + dout("unrecognized con state %d\n", (int)con->state); + con->error_msg = "unrecognized con state"; + BUG(); + } goto fault; + } if (test_and_clear_bit(CON_FLAG_BACKOFF, &con->flags)) { dout("con_work %p backing off\n", con); - ret = queue_con_delay(con, round_jiffies_relative(con->delay)); - if (ret) { + if (queue_delayed_work(ceph_msgr_wq, &con->work, + round_jiffies_relative(con->delay))) { + dout("con_work %p backoff %lu\n", con, con->delay); + mutex_unlock(&con->mutex); + return; + } else { dout("con_work %p FAILED to back off %lu\n", con, con->delay); - BUG_ON(ret == -ENOENT); set_bit(CON_FLAG_BACKOFF, &con->flags); } goto done; @@ -2377,7 +2356,7 @@ static void con_work(struct work_struct *work) static void ceph_fault(struct ceph_connection *con) __releases(con->mutex) { - pr_warning("%s%lld %s %s\n", ENTITY_NAME(con->peer_name), + pr_err("%s%lld %s %s\n", ENTITY_NAME(con->peer_name), ceph_pr_addr(&con->peer_addr.in_addr), con->error_msg); dout("fault %p state %lu to peer %s\n", con, con->state, ceph_pr_addr(&con->peer_addr.in_addr)); @@ -2419,8 +2398,24 @@ static void ceph_fault(struct ceph_connection *con) con->delay = BASE_DELAY_INTERVAL; else if (con->delay < MAX_DELAY_INTERVAL) con->delay *= 2; - set_bit(CON_FLAG_BACKOFF, &con->flags); - queue_con(con); + con->ops->get(con); + if (queue_delayed_work(ceph_msgr_wq, &con->work, + round_jiffies_relative(con->delay))) { + dout("fault queued %p delay %lu\n", con, con->delay); + } else { + con->ops->put(con); + dout("fault failed to queue %p delay %lu, backoff\n", + con, con->delay); + /* + * In many cases we see a socket state change + * while con_work is running and end up + * queuing (non-delayed) work, such that we + * can't backoff with a delay. Set a flag so + * that when con_work restarts we schedule the + * delay then. + */ + set_bit(CON_FLAG_BACKOFF, &con->flags); + } } out_unlock: diff --git a/trunk/net/ceph/osd_client.c b/trunk/net/ceph/osd_client.c index 780caf6b0491..c1d756cc7448 100644 --- a/trunk/net/ceph/osd_client.c +++ b/trunk/net/ceph/osd_client.c @@ -221,7 +221,6 @@ struct ceph_osd_request *ceph_osdc_alloc_request(struct ceph_osd_client *osdc, kref_init(&req->r_kref); init_completion(&req->r_completion); init_completion(&req->r_safe_completion); - RB_CLEAR_NODE(&req->r_node); INIT_LIST_HEAD(&req->r_unsafe_item); INIT_LIST_HEAD(&req->r_linger_item); INIT_LIST_HEAD(&req->r_linger_osd); @@ -581,7 +580,7 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc, dout("__kick_osd_requests osd%d\n", osd->o_osd); err = __reset_osd(osdc, osd); - if (err) + if (err == -EAGAIN) return; list_for_each_entry(req, &osd->o_requests, r_osd_item) { @@ -608,6 +607,14 @@ static void __kick_osd_requests(struct ceph_osd_client *osdc, } } +static void kick_osd_requests(struct ceph_osd_client *osdc, + struct ceph_osd *kickosd) +{ + mutex_lock(&osdc->request_mutex); + __kick_osd_requests(osdc, kickosd); + mutex_unlock(&osdc->request_mutex); +} + /* * If the osd connection drops, we need to resubmit all requests. */ @@ -621,9 +628,7 @@ static void osd_reset(struct ceph_connection *con) dout("osd_reset osd%d\n", osd->o_osd); osdc = osd->o_osdc; down_read(&osdc->map_sem); - mutex_lock(&osdc->request_mutex); - __kick_osd_requests(osdc, osd); - mutex_unlock(&osdc->request_mutex); + kick_osd_requests(osdc, osd); send_queued(osdc); up_read(&osdc->map_sem); } @@ -642,7 +647,6 @@ static struct ceph_osd *create_osd(struct ceph_osd_client *osdc, int onum) atomic_set(&osd->o_ref, 1); osd->o_osdc = osdc; osd->o_osd = onum; - RB_CLEAR_NODE(&osd->o_node); INIT_LIST_HEAD(&osd->o_requests); INIT_LIST_HEAD(&osd->o_linger_requests); INIT_LIST_HEAD(&osd->o_osd_lru); @@ -746,7 +750,6 @@ static int __reset_osd(struct ceph_osd_client *osdc, struct ceph_osd *osd) if (list_empty(&osd->o_requests) && list_empty(&osd->o_linger_requests)) { __remove_osd(osdc, osd); - ret = -ENODEV; } else if (memcmp(&osdc->osdmap->osd_addr[osd->o_osd], &osd->o_con.peer_addr, sizeof(osd->o_con.peer_addr)) == 0 && @@ -873,9 +876,9 @@ static void __unregister_request(struct ceph_osd_client *osdc, req->r_osd = NULL; } - list_del_init(&req->r_req_lru_item); ceph_osdc_put_request(req); + list_del_init(&req->r_req_lru_item); if (osdc->num_requests == 0) { dout(" no requests, canceling timeout\n"); __cancel_osd_timeout(osdc); @@ -907,8 +910,8 @@ static void __unregister_linger_request(struct ceph_osd_client *osdc, struct ceph_osd_request *req) { dout("__unregister_linger_request %p\n", req); - list_del_init(&req->r_linger_item); if (req->r_osd) { + list_del_init(&req->r_linger_item); list_del_init(&req->r_linger_osd); if (list_empty(&req->r_osd->o_requests) && @@ -1087,10 +1090,12 @@ static void handle_timeout(struct work_struct *work) { struct ceph_osd_client *osdc = container_of(work, struct ceph_osd_client, timeout_work.work); - struct ceph_osd_request *req; + struct ceph_osd_request *req, *last_req = NULL; struct ceph_osd *osd; + unsigned long timeout = osdc->client->options->osd_timeout * HZ; unsigned long keepalive = osdc->client->options->osd_keepalive_timeout * HZ; + unsigned long last_stamp = 0; struct list_head slow_osds; dout("timeout\n"); down_read(&osdc->map_sem); @@ -1099,6 +1104,37 @@ static void handle_timeout(struct work_struct *work) mutex_lock(&osdc->request_mutex); + /* + * reset osds that appear to be _really_ unresponsive. this + * is a failsafe measure.. we really shouldn't be getting to + * this point if the system is working properly. the monitors + * should mark the osd as failed and we should find out about + * it from an updated osd map. + */ + while (timeout && !list_empty(&osdc->req_lru)) { + req = list_entry(osdc->req_lru.next, struct ceph_osd_request, + r_req_lru_item); + + /* hasn't been long enough since we sent it? */ + if (time_before(jiffies, req->r_stamp + timeout)) + break; + + /* hasn't been long enough since it was acked? */ + if (req->r_request->ack_stamp == 0 || + time_before(jiffies, req->r_request->ack_stamp + timeout)) + break; + + BUG_ON(req == last_req && req->r_stamp == last_stamp); + last_req = req; + last_stamp = req->r_stamp; + + osd = req->r_osd; + BUG_ON(!osd); + pr_warning(" tid %llu timed out on osd%d, will reset osd\n", + req->r_tid, osd->o_osd); + __kick_osd_requests(osdc, osd); + } + /* * ping osds that are a bit slow. this ensures that if there * is a break in the TCP connection we will notice, and reopen @@ -1328,8 +1364,8 @@ static void kick_requests(struct ceph_osd_client *osdc, int force_resend) dout("kicking lingering %p tid %llu osd%d\n", req, req->r_tid, req->r_osd ? req->r_osd->o_osd : -1); - __register_request(osdc, req); __unregister_linger_request(osdc, req); + __register_request(osdc, req); } mutex_unlock(&osdc->request_mutex); @@ -1563,7 +1599,6 @@ int ceph_osdc_create_event(struct ceph_osd_client *osdc, event->data = data; event->osdc = osdc; INIT_LIST_HEAD(&event->osd_node); - RB_CLEAR_NODE(&event->node); kref_init(&event->kref); /* one ref for us */ kref_get(&event->kref); /* one ref for the caller */ init_completion(&event->completion); diff --git a/trunk/net/ceph/osdmap.c b/trunk/net/ceph/osdmap.c index de73214b5d26..5433fb0eb3c6 100644 --- a/trunk/net/ceph/osdmap.c +++ b/trunk/net/ceph/osdmap.c @@ -469,22 +469,6 @@ static struct ceph_pg_pool_info *__lookup_pg_pool(struct rb_root *root, int id) return NULL; } -const char *ceph_pg_pool_name_by_id(struct ceph_osdmap *map, u64 id) -{ - struct ceph_pg_pool_info *pi; - - if (id == CEPH_NOPOOL) - return NULL; - - if (WARN_ON_ONCE(id > (u64) INT_MAX)) - return NULL; - - pi = __lookup_pg_pool(&map->pg_pools, (int) id); - - return pi ? pi->name : NULL; -} -EXPORT_SYMBOL(ceph_pg_pool_name_by_id); - int ceph_pg_poolid_by_name(struct ceph_osdmap *map, const char *name) { struct rb_node *rbp; @@ -661,12 +645,10 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) ceph_decode_32_safe(p, end, max, bad); while (max--) { ceph_decode_need(p, end, 4 + 1 + sizeof(pi->v), bad); - err = -ENOMEM; pi = kzalloc(sizeof(*pi), GFP_NOFS); if (!pi) goto bad; pi->id = ceph_decode_32(p); - err = -EINVAL; ev = ceph_decode_8(p); /* encoding version */ if (ev > CEPH_PG_POOL_VERSION) { pr_warning("got unknown v %d > %d of ceph_pg_pool\n", @@ -682,13 +664,8 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) __insert_pg_pool(&map->pg_pools, pi); } - if (version >= 5) { - err = __decode_pool_names(p, end, map); - if (err < 0) { - dout("fail to decode pool names"); - goto bad; - } - } + if (version >= 5 && __decode_pool_names(p, end, map) < 0) + goto bad; ceph_decode_32_safe(p, end, map->pool_max, bad); @@ -768,7 +745,7 @@ struct ceph_osdmap *osdmap_decode(void **p, void *end) return map; bad: - dout("osdmap_decode fail err %d\n", err); + dout("osdmap_decode fail\n"); ceph_osdmap_destroy(map); return ERR_PTR(err); } @@ -862,7 +839,6 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, if (ev > CEPH_PG_POOL_VERSION) { pr_warning("got unknown v %d > %d of ceph_pg_pool\n", ev, CEPH_PG_POOL_VERSION); - err = -EINVAL; goto bad; } pi = __lookup_pg_pool(&map->pg_pools, pool); @@ -879,11 +855,8 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, if (err < 0) goto bad; } - if (version >= 5) { - err = __decode_pool_names(p, end, map); - if (err < 0) - goto bad; - } + if (version >= 5 && __decode_pool_names(p, end, map) < 0) + goto bad; /* old_pool */ ceph_decode_32_safe(p, end, len, bad); @@ -959,13 +932,15 @@ struct ceph_osdmap *osdmap_apply_incremental(void **p, void *end, (void) __remove_pg_mapping(&map->pg_temp, pgid); /* insert */ - err = -EINVAL; - if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32)) + if (pglen > (UINT_MAX - sizeof(*pg)) / sizeof(u32)) { + err = -EINVAL; goto bad; - err = -ENOMEM; + } pg = kmalloc(sizeof(*pg) + sizeof(u32)*pglen, GFP_NOFS); - if (!pg) + if (!pg) { + err = -ENOMEM; goto bad; + } pg->pgid = pgid; pg->len = pglen; for (j = 0; j < pglen; j++) diff --git a/trunk/net/sunrpc/rpcb_clnt.c b/trunk/net/sunrpc/rpcb_clnt.c index 795a0f4e920b..411f332de0b3 100644 --- a/trunk/net/sunrpc/rpcb_clnt.c +++ b/trunk/net/sunrpc/rpcb_clnt.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include diff --git a/trunk/net/sunrpc/svc.c b/trunk/net/sunrpc/svc.c index dbf12ac5ecb7..dfa4ba69ff45 100644 --- a/trunk/net/sunrpc/svc.c +++ b/trunk/net/sunrpc/svc.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -1040,7 +1041,7 @@ static void svc_unregister(const struct svc_serv *serv, struct net *net) } /* - * dprintk the given error with the address of the client that caused it. + * Printk the given error with the address of the client that caused it. */ static __printf(2, 3) void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) @@ -1054,7 +1055,8 @@ void svc_printk(struct svc_rqst *rqstp, const char *fmt, ...) vaf.fmt = fmt; vaf.va = &args; - dprintk("svc: %s: %pV", svc_print_addr(rqstp, buf, sizeof(buf)), &vaf); + net_warn_ratelimited("svc: %s: %pV", + svc_print_addr(rqstp, buf, sizeof(buf)), &vaf); va_end(args); } @@ -1303,7 +1305,7 @@ svc_process(struct svc_rqst *rqstp) * Setup response xdr_buf. * Initially it has just one page */ - rqstp->rq_next_page = &rqstp->rq_respages[1]; + rqstp->rq_resused = 1; resv->iov_base = page_address(rqstp->rq_respages[0]); resv->iov_len = 0; rqstp->rq_res.pages = rqstp->rq_respages + 1; diff --git a/trunk/net/sunrpc/svcsock.c b/trunk/net/sunrpc/svcsock.c index 0a148c9d2a5c..cc3020d16789 100644 --- a/trunk/net/sunrpc/svcsock.c +++ b/trunk/net/sunrpc/svcsock.c @@ -605,7 +605,6 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) rqstp->rq_respages = rqstp->rq_pages + 1 + DIV_ROUND_UP(rqstp->rq_arg.page_len, PAGE_SIZE); } - rqstp->rq_next_page = rqstp->rq_respages+1; if (serv->sv_stats) serv->sv_stats->netudpcnt++; @@ -879,9 +878,9 @@ static unsigned int svc_tcp_restore_pages(struct svc_sock *svsk, struct svc_rqst { unsigned int i, len, npages; - if (svsk->sk_datalen == 0) + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) return 0; - len = svsk->sk_datalen; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; for (i = 0; i < npages; i++) { if (rqstp->rq_pages[i] != NULL) @@ -898,9 +897,9 @@ static void svc_tcp_save_pages(struct svc_sock *svsk, struct svc_rqst *rqstp) { unsigned int i, len, npages; - if (svsk->sk_datalen == 0) + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) return; - len = svsk->sk_datalen; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; for (i = 0; i < npages; i++) { svsk->sk_pages[i] = rqstp->rq_pages[i]; @@ -912,9 +911,9 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk) { unsigned int i, len, npages; - if (svsk->sk_datalen == 0) + if (svsk->sk_tcplen <= sizeof(rpc_fraghdr)) goto out; - len = svsk->sk_datalen; + len = svsk->sk_tcplen - sizeof(rpc_fraghdr); npages = (len + PAGE_SIZE - 1) >> PAGE_SHIFT; for (i = 0; i < npages; i++) { BUG_ON(svsk->sk_pages[i] == NULL); @@ -923,12 +922,13 @@ static void svc_tcp_clear_pages(struct svc_sock *svsk) } out: svsk->sk_tcplen = 0; - svsk->sk_datalen = 0; } /* - * Receive fragment record header. + * Receive data. * If we haven't gotten the record length yet, get the next four bytes. + * Otherwise try to gobble up as much as possible up to the complete + * record length. */ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) { @@ -954,16 +954,32 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) return -EAGAIN; } - dprintk("svc: TCP record, %d bytes\n", svc_sock_reclen(svsk)); - if (svc_sock_reclen(svsk) + svsk->sk_datalen > - serv->sv_max_mesg) { - net_notice_ratelimited("RPC: fragment too large: %d\n", - svc_sock_reclen(svsk)); + svsk->sk_reclen = ntohl(svsk->sk_reclen); + if (!(svsk->sk_reclen & RPC_LAST_STREAM_FRAGMENT)) { + /* FIXME: technically, a record can be fragmented, + * and non-terminal fragments will not have the top + * bit set in the fragment length header. + * But apparently no known nfs clients send fragmented + * records. */ + net_notice_ratelimited("RPC: multiple fragments per record not supported\n"); + goto err_delete; + } + + svsk->sk_reclen &= RPC_FRAGMENT_SIZE_MASK; + dprintk("svc: TCP record, %d bytes\n", svsk->sk_reclen); + if (svsk->sk_reclen > serv->sv_max_mesg) { + net_notice_ratelimited("RPC: fragment too large: 0x%08lx\n", + (unsigned long)svsk->sk_reclen); goto err_delete; } } - return svc_sock_reclen(svsk); + if (svsk->sk_reclen < 8) + goto err_delete; /* client is nuts. */ + + len = svsk->sk_reclen; + + return len; error: dprintk("RPC: TCP recv_record got %d\n", len); return len; @@ -1007,7 +1023,7 @@ static int receive_cb_reply(struct svc_sock *svsk, struct svc_rqst *rqstp) if (dst->iov_len < src->iov_len) return -EAGAIN; /* whatever; just giving up. */ memcpy(dst->iov_base, src->iov_base, src->iov_len); - xprt_complete_rqst(req->rq_task, rqstp->rq_arg.len); + xprt_complete_rqst(req->rq_task, svsk->sk_reclen); rqstp->rq_arg.len = 0; return 0; } @@ -1026,17 +1042,6 @@ static int copy_pages_to_kvecs(struct kvec *vec, struct page **pages, int len) return i; } -static void svc_tcp_fragment_received(struct svc_sock *svsk) -{ - /* If we have more data, signal svc_xprt_enqueue() to try again */ - if (svc_recv_available(svsk) > sizeof(rpc_fraghdr)) - set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); - dprintk("svc: TCP %s record (%d bytes)\n", - svc_sock_final_rec(svsk) ? "final" : "nonfinal", - svc_sock_reclen(svsk)); - svsk->sk_tcplen = 0; - svsk->sk_reclen = 0; -} /* * Receive data from a TCP socket. @@ -1063,39 +1068,29 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) goto error; base = svc_tcp_restore_pages(svsk, rqstp); - want = svc_sock_reclen(svsk) - (svsk->sk_tcplen - sizeof(rpc_fraghdr)); + want = svsk->sk_reclen - base; vec = rqstp->rq_vec; pnum = copy_pages_to_kvecs(&vec[0], &rqstp->rq_pages[0], - svsk->sk_datalen + want); + svsk->sk_reclen); rqstp->rq_respages = &rqstp->rq_pages[pnum]; - rqstp->rq_next_page = rqstp->rq_respages + 1; /* Now receive data */ len = svc_partial_recvfrom(rqstp, vec, pnum, want, base); - if (len >= 0) { + if (len >= 0) svsk->sk_tcplen += len; - svsk->sk_datalen += len; - } - if (len != want || !svc_sock_final_rec(svsk)) { + if (len != want) { svc_tcp_save_pages(svsk, rqstp); if (len < 0 && len != -EAGAIN) - goto err_delete; - if (len == want) - svc_tcp_fragment_received(svsk); - else - dprintk("svc: incomplete TCP record (%d of %d)\n", - (int)(svsk->sk_tcplen - sizeof(rpc_fraghdr)), - svc_sock_reclen(svsk)); + goto err_other; + dprintk("svc: incomplete TCP record (%d of %d)\n", + svsk->sk_tcplen, svsk->sk_reclen); goto err_noclose; } - if (svc_sock_reclen(svsk) < 8) - goto err_delete; /* client is nuts. */ - - rqstp->rq_arg.len = svsk->sk_datalen; + rqstp->rq_arg.len = svsk->sk_reclen; rqstp->rq_arg.page_base = 0; if (rqstp->rq_arg.len <= rqstp->rq_arg.head[0].iov_len) { rqstp->rq_arg.head[0].iov_len = rqstp->rq_arg.len; @@ -1112,8 +1107,11 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) len = receive_cb_reply(svsk, rqstp); /* Reset TCP read info */ - svsk->sk_datalen = 0; - svc_tcp_fragment_received(svsk); + svsk->sk_reclen = 0; + svsk->sk_tcplen = 0; + /* If we have more data, signal svc_xprt_enqueue() to try again */ + if (svc_recv_available(svsk) > sizeof(rpc_fraghdr)) + set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); if (len < 0) goto error; @@ -1122,14 +1120,15 @@ static int svc_tcp_recvfrom(struct svc_rqst *rqstp) if (serv->sv_stats) serv->sv_stats->nettcpcnt++; + dprintk("svc: TCP complete record (%d bytes)\n", rqstp->rq_arg.len); return rqstp->rq_arg.len; error: if (len != -EAGAIN) - goto err_delete; + goto err_other; dprintk("RPC: TCP recvfrom got EAGAIN\n"); return 0; -err_delete: +err_other: printk(KERN_NOTICE "%s: recvfrom returned errno %d\n", svsk->sk_xprt.xpt_server->sv_name, -len); set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); @@ -1306,7 +1305,6 @@ static void svc_tcp_init(struct svc_sock *svsk, struct svc_serv *serv) svsk->sk_reclen = 0; svsk->sk_tcplen = 0; - svsk->sk_datalen = 0; memset(&svsk->sk_pages[0], 0, sizeof(svsk->sk_pages)); tcp_sk(sk)->nonagle |= TCP_NAGLE_OFF; diff --git a/trunk/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/trunk/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 0ce75524ed21..41cb63b623df 100644 --- a/trunk/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/trunk/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -521,11 +521,11 @@ static int rdma_read_xdr(struct svcxprt_rdma *xprt, rqstp->rq_pages[ch_no] = NULL; /* - * Detach res pages. If svc_release sees any it will attempt to - * put them. + * Detach res pages. svc_release must see a resused count of + * zero or it will attempt to put them. */ - while (rqstp->rq_next_page != rqstp->rq_respages) - *(--rqstp->rq_next_page) = NULL; + while (rqstp->rq_resused) + rqstp->rq_respages[--rqstp->rq_resused] = NULL; return err; } @@ -550,7 +550,7 @@ static int rdma_read_complete(struct svc_rqst *rqstp, /* rq_respages starts after the last arg page */ rqstp->rq_respages = &rqstp->rq_arg.pages[page_no]; - rqstp->rq_next_page = &rqstp->rq_arg.pages[page_no]; + rqstp->rq_resused = 0; /* Rebuild rq_arg head and tail. */ rqstp->rq_arg.head[0] = head->arg.head[0]; diff --git a/trunk/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/trunk/net/sunrpc/xprtrdma/svc_rdma_sendto.c index c1d124dc772b..42eb7ba0b903 100644 --- a/trunk/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/trunk/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -548,7 +548,6 @@ static int send_reply(struct svcxprt_rdma *rdma, int sge_no; int sge_bytes; int page_no; - int pages; int ret; /* Post a recv buffer to handle another request. */ @@ -612,8 +611,7 @@ static int send_reply(struct svcxprt_rdma *rdma, * respages array. They are our pages until the I/O * completes. */ - pages = rqstp->rq_next_page - rqstp->rq_respages; - for (page_no = 0; page_no < pages; page_no++) { + for (page_no = 0; page_no < rqstp->rq_resused; page_no++) { ctxt->pages[page_no+1] = rqstp->rq_respages[page_no]; ctxt->count++; rqstp->rq_respages[page_no] = NULL; diff --git a/trunk/scripts/coccinelle/misc/warn.cocci b/trunk/scripts/coccinelle/misc/warn.cocci deleted file mode 100644 index fda8c3558e4f..000000000000 --- a/trunk/scripts/coccinelle/misc/warn.cocci +++ /dev/null @@ -1,109 +0,0 @@ -/// Use WARN(1,...) rather than printk followed by WARN_ON(1) -/// -// Confidence: High -// Copyright: (C) 2012 Julia Lawall, INRIA/LIP6. GPLv2. -// Copyright: (C) 2012 Gilles Muller, INRIA/LiP6. GPLv2. -// URL: http://coccinelle.lip6.fr/ -// Comments: -// Options: -no_includes -include_headers - -virtual patch -virtual context -virtual org -virtual report - -@bad1@ -position p; -@@ - -printk(...); -printk@p(...); -WARN_ON(1); - -@r1 depends on context || report || org@ -position p != bad1.p; -@@ - - printk@p(...); -*WARN_ON(1); - -@script:python depends on org@ -p << r1.p; -@@ - -cocci.print_main("printk + WARN_ON can be just WARN",p) - -@script:python depends on report@ -p << r1.p; -@@ - -msg = "SUGGESTION: printk + WARN_ON can be just WARN" -coccilib.report.print_report(p[0],msg) - -@ok1 depends on patch@ -expression list es; -position p != bad1.p; -@@ - --printk@p( -+WARN(1, - es); --WARN_ON(1); - -@depends on patch@ -expression list ok1.es; -@@ - -if (...) -- { - WARN(1,es); -- } - -// -------------------------------------------------------------------- - -@bad2@ -position p; -@@ - -printk(...); -printk@p(...); -WARN_ON_ONCE(1); - -@r2 depends on context || report || org@ -position p != bad1.p; -@@ - - printk@p(...); -*WARN_ON_ONCE(1); - -@script:python depends on org@ -p << r2.p; -@@ - -cocci.print_main("printk + WARN_ON_ONCE can be just WARN_ONCE",p) - -@script:python depends on report@ -p << r2.p; -@@ - -msg = "SUGGESTION: printk + WARN_ON_ONCE can be just WARN_ONCE" -coccilib.report.print_report(p[0],msg) - -@ok2 depends on patch@ -expression list es; -position p != bad2.p; -@@ - --printk@p( -+WARN_ONCE(1, - es); --WARN_ON_ONCE(1); - -@depends on patch@ -expression list ok2.es; -@@ - -if (...) -- { - WARN_ONCE(1,es); -- } diff --git a/trunk/scripts/config b/trunk/scripts/config index bb4d3deb6d1c..ee355394f4ef 100755 --- a/trunk/scripts/config +++ b/trunk/scripts/config @@ -101,6 +101,7 @@ while [ "$1" != "" ] ; do case "$CMD" in --keep-case|-k) MUNGE_CASE=no + shift continue ;; --refresh) diff --git a/trunk/scripts/pnmtologo.c b/trunk/scripts/pnmtologo.c index 68bb4efc5af4..5c113123ed9f 100644 --- a/trunk/scripts/pnmtologo.c +++ b/trunk/scripts/pnmtologo.c @@ -74,7 +74,6 @@ static unsigned int logo_height; static struct color **logo_data; static struct color logo_clut[MAX_LINUX_LOGO_COLORS]; static unsigned int logo_clutsize; -static int is_plain_pbm = 0; static void die(const char *fmt, ...) __attribute__ ((noreturn)) __attribute ((format (printf, 1, 2))); @@ -104,11 +103,6 @@ static unsigned int get_number(FILE *fp) val = 0; while (isdigit(c)) { val = 10*val+c-'0'; - /* some PBM are 'broken'; GiMP for example exports a PBM without space - * between the digits. This is Ok cause we know a PBM can only have a '1' - * or a '0' for the digit. */ - if (is_plain_pbm) - break; c = fgetc(fp); if (c == EOF) die("%s: end of file\n", filename); @@ -173,7 +167,6 @@ static void read_image(void) switch (magic) { case '1': /* Plain PBM */ - is_plain_pbm = 1; for (i = 0; i < logo_height; i++) for (j = 0; j < logo_width; j++) logo_data[i][j].red = logo_data[i][j].green = diff --git a/trunk/scripts/tags.sh b/trunk/scripts/tags.sh index 08f06c00745e..79fdafb0d263 100755 --- a/trunk/scripts/tags.sh +++ b/trunk/scripts/tags.sh @@ -48,14 +48,13 @@ find_arch_sources() for i in $archincludedir; do prune="$prune -wholename $i -prune -o" done - find ${tree}arch/$1 $ignore $subarchprune $prune -name "$2" -print; + find ${tree}arch/$1 $ignore $prune -name "$2" -print; } # find sources in arch/$1/include find_arch_include_sources() { - include=$(find ${tree}arch/$1/ $subarchprune \ - -name include -type d -print); + include=$(find ${tree}arch/$1/ -name include -type d); if [ -n "$include" ]; then archincludedir="$archincludedir $include" find $include $ignore -name "$2" -print; @@ -96,32 +95,6 @@ all_sources() find_other_sources '*.[chS]' } -all_compiled_sources() -{ - for i in $(all_sources); do - case "$i" in - *.[cS]) - j=${i/\.[cS]/\.o} - if [ -e $j ]; then - echo $i - fi - ;; - *) - echo $i - ;; - esac - done -} - -all_target_sources() -{ - if [ -n "$COMPILED_SOURCE" ]; then - all_compiled_sources - else - all_sources - fi -} - all_kconfigs() { for arch in $ALLSOURCE_ARCHS; do @@ -137,18 +110,18 @@ all_defconfigs() docscope() { - (echo \-k; echo \-q; all_target_sources) > cscope.files + (echo \-k; echo \-q; all_sources) > cscope.files cscope -b -f cscope.out } dogtags() { - all_target_sources | gtags -i -f - + all_sources | gtags -i -f - } exuberant() { - all_target_sources | xargs $1 -a \ + all_sources | xargs $1 -a \ -I __initdata,__exitdata,__acquires,__releases \ -I __read_mostly,____cacheline_aligned \ -I ____cacheline_aligned_in_smp \ @@ -200,7 +173,7 @@ exuberant() emacs() { - all_target_sources | xargs $1 -a \ + all_sources | xargs $1 -a \ --regex='/^(ENTRY|_GLOBAL)(\([^)]*\)).*/\2/' \ --regex='/^SYSCALL_DEFINE[0-9]?(\([^,)]*\).*/sys_\1/' \ --regex='/^TRACE_EVENT(\([^,)]*\).*/trace_\1/' \ @@ -247,10 +220,11 @@ xtags() elif $1 --version 2>&1 | grep -iq emacs; then emacs $1 else - all_target_sources | xargs $1 -a + all_sources | xargs $1 -a fi } + # Support um (which uses SUBARCH) if [ "${ARCH}" = "um" ]; then if [ "$SUBARCH" = "i386" ]; then @@ -260,21 +234,6 @@ if [ "${ARCH}" = "um" ]; then else archinclude=${SUBARCH} fi -elif [ "${SRCARCH}" = "arm" -a "${SUBARCH}" != "" ]; then - subarchdir=$(find ${tree}arch/$SRCARCH/ -name "mach-*" -type d -o \ - -name "plat-*" -type d); - for i in $subarchdir; do - case "$i" in - *"mach-"${SUBARCH}) - ;; - *"plat-"${SUBARCH}) - ;; - *) - subarchprune="$subarchprune \ - -wholename $i -prune -o" - ;; - esac - done fi remove_structs=