Merge remote-tracking branch 'sm8250/lineage-20' into lineage-21
* sm8250/lineage-20: BACKPORT: erofs: fix lz4 inplace decompression ANDROID: Enable percpu high priority kthreads for erofs UPSTREAM: erofs: fix an error code in z_erofs_init_zip_subsystem() BACKPORT: erofs: add per-cpu threads for decompression as an option sched: Provide sched_set_fifo() ANDROID: GKI: enable CONFIG_EROFS_FS=y Revert "erofs: add REQ_RAHEAD flag to readahead requests" erofs: Switch to attach/detach_page_private include/linux/pagemap.h: introduce attach/detach_page_private BACKPORT: erofs: fix use-after-free of on-stack io[] MAINTAINERS: erofs: update my email address MAINTAINERS: erofs: complete sub-entries for erofs erofs: move erofs out of staging staging: erofs: turn cache strategies into mount options staging: erofs: kill CONFIG_EROFS_FS_USE_VM_MAP_RAM staging: erofs: support IO read error injection staging: erofs: complete POSIX ACL support staging: erofs: add document Reapply "erofs: support decompress big pcluster for lz4 backend" Revert "erofs: fix lz4 inplace decompression" Revert "Reapply "cred: switch to using atomic_long_t"" Reapply "cred: switch to using atomic_long_t" BACKPORT: net: core: enable SO_BINDTODEVICE for non-root users tcp: Add memory barrier to tcp_push() tracing: Ensure visibility when inserting an element into tracing_map net/rds: Fix UBSAN: array-index-out-of-bounds in rds_cmsg_recv llc: Drop support for ETH_P_TR_802_2. llc: make llc_ui_sendmsg() more robust against bonding changes vlan: skip nested type that is not IFLA_VLAN_QOS_MAPPING net/smc: fix illegal rmb_desc access in SMC-D connection dump drivers: core: fix kernel-doc markup for dev_err_probe() driver code: print symbolic error code block: Remove special-casing of compound pages Revert "driver core: Annotate dev_err_probe() with __must_check" nouveau/vmm: don't set addr on the fail path to avoid warning driver core: Annotate dev_err_probe() with __must_check parisc/firmware: Fix F-extend for PDC addresses x86/CPU/AMD: Fix disabling XSAVES on AMD family 0x17 due to erratum rpmsg: virtio: Free driver_override when rpmsg_remove() powerpc: Use always instead of always-y in for crtsavres.o hwrng: core - Fix page fault dead lock on mmap-ed hwrng PM: hibernate: Enforce ordering during image compression/decompression crypto: api - Disallow identical driver names ext4: allow for the last group to be marked as trimmed serial: sc16is7xx: add check for unsupported SPI modes during probe spi: introduce SPI_MODE_X_MASK macro driver core: add device probe log helper serial: sc16is7xx: set safe default SPI clock frequency units: add the HZ macros units: change from 'L' to 'UL' units: Add Watt units include/linux/units.h: add helpers for kelvin to/from Celsius conversion PCI: mediatek: Clear interrupt status before dispatching handler soc: qcom: smem: Add boundary checks for partitions clk: qcom: gcc-sdm660: Add SDCC1 BCR clocks for SDM660 dt-bindings: clock: Add support for SDCC BCR id for SDM660 exfat: github action: run buiuld and tests on for-kernel-version-from-4.1.0 branch exfat: fix ALIGN_DOWN undefined error exfat: using ffs instead of internal logic exfat: using hweight instead of internal logic exfat: fix ctime is not updated exfat: fix setting uninitialized time to ctime/atime exfat: convert to new timestamp accessors exfat: convert to ctime accessor functions exfat: fs: pass the request_mask to generic_fillattr exfat: convert to simple_rename_timestamp exfat: ensure that ctime is updated whenever the mtime is exfat: fs: add CONFIG_BUFFER_HEAD exfat: use fat ioctls definitions from include/uapi/linux/msdos_fs.h exfat: github action: remove liunx-4.1 source to get more disk space exfat: support create zero-size directory exfat: support handle zero-size directory exfat: add ioctls for accessing attributes exfat: vfs: get rid of old '->iterate' directory operation Change-Id: I382edb2b4faa25ea8e11ba4b916cb2520b6ec35d
This commit is contained in:
54 files changed
+1361
-369
No files matched your search
@@ -0,0 +1,219 @@
|
||||
Overview
|
||||
========
|
||||
|
||||
EROFS file-system stands for Enhanced Read-Only File System. Different
|
||||
from other read-only file systems, it aims to be designed for flexibility,
|
||||
scalability, but be kept simple and high performance.
|
||||
|
||||
It is designed as a better filesystem solution for the following scenarios:
|
||||
- read-only storage media or
|
||||
|
||||
- part of a fully trusted read-only solution, which means it needs to be
|
||||
immutable and bit-for-bit identical to the official golden image for
|
||||
their releases due to security and other considerations and
|
||||
|
||||
- hope to save some extra storage space with guaranteed end-to-end performance
|
||||
by using reduced metadata and transparent file compression, especially
|
||||
for those embedded devices with limited memory (ex, smartphone);
|
||||
|
||||
Here is the main features of EROFS:
|
||||
- Little endian on-disk design;
|
||||
|
||||
- Currently 4KB block size (nobh) and therefore maximum 16TB address space;
|
||||
|
||||
- Metadata & data could be mixed by design;
|
||||
|
||||
- 2 inode versions for different requirements:
|
||||
v1 v2
|
||||
Inode metadata size: 32 bytes 64 bytes
|
||||
Max file size: 4 GB 16 EB (also limited by max. vol size)
|
||||
Max uids/gids: 65536 4294967296
|
||||
File creation time: no yes (64 + 32-bit timestamp)
|
||||
Max hardlinks: 65536 4294967296
|
||||
Metadata reserved: 4 bytes 14 bytes
|
||||
|
||||
- Support extended attributes (xattrs) as an option;
|
||||
|
||||
- Support xattr inline and tail-end data inline for all files;
|
||||
|
||||
- Support POSIX.1e ACLs by using xattrs;
|
||||
|
||||
- Support transparent file compression as an option:
|
||||
LZ4 algorithm with 4 KB fixed-output compression for high performance;
|
||||
|
||||
The following git tree provides the file system user-space tools under
|
||||
development (ex, formatting tool mkfs.erofs):
|
||||
>> git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs-utils.git
|
||||
|
||||
Bugs and patches are welcome, please kindly help us and send to the following
|
||||
linux-erofs mailing list:
|
||||
>> linux-erofs mailing list <linux-erofs@lists.ozlabs.org>
|
||||
|
||||
Mount options
|
||||
=============
|
||||
|
||||
fault_injection=%d Enable fault injection in all supported types with
|
||||
specified injection rate. Supported injection type:
|
||||
Type_Name Type_Value
|
||||
FAULT_KMALLOC 0x000000001
|
||||
FAULT_READ_IO 0x000000002
|
||||
(no)user_xattr Setup Extended User Attributes. Note: xattr is enabled
|
||||
by default if CONFIG_EROFS_FS_XATTR is selected.
|
||||
(no)acl Setup POSIX Access Control List. Note: acl is enabled
|
||||
by default if CONFIG_EROFS_FS_POSIX_ACL is selected.
|
||||
cache_strategy=%s Select a strategy for cached decompression from now on:
|
||||
disabled: In-place I/O decompression only;
|
||||
readahead: Cache the last incomplete compressed physical
|
||||
cluster for further reading. It still does
|
||||
in-place I/O decompression for the rest
|
||||
compressed physical clusters;
|
||||
readaround: Cache the both ends of incomplete compressed
|
||||
physical clusters for further reading.
|
||||
It still does in-place I/O decompression
|
||||
for the rest compressed physical clusters.
|
||||
|
||||
Module parameters
|
||||
=================
|
||||
use_vmap=[0|1] Use vmap() instead of vm_map_ram() (default 0).
|
||||
|
||||
On-disk details
|
||||
===============
|
||||
|
||||
Summary
|
||||
-------
|
||||
Different from other read-only file systems, an EROFS volume is designed
|
||||
to be as simple as possible:
|
||||
|
||||
|-> aligned with the block size
|
||||
____________________________________________________________
|
||||
| |SB| | ... | Metadata | ... | Data | Metadata | ... | Data |
|
||||
|_|__|_|_____|__________|_____|______|__________|_____|______|
|
||||
0 +1K
|
||||
|
||||
All data areas should be aligned with the block size, but metadata areas
|
||||
may not. All metadatas can be now observed in two different spaces (views):
|
||||
1. Inode metadata space
|
||||
Each valid inode should be aligned with an inode slot, which is a fixed
|
||||
value (32 bytes) and designed to be kept in line with v1 inode size.
|
||||
|
||||
Each inode can be directly found with the following formula:
|
||||
inode offset = meta_blkaddr * block_size + 32 * nid
|
||||
|
||||
|-> aligned with 8B
|
||||
|-> followed closely
|
||||
+ meta_blkaddr blocks |-> another slot
|
||||
_____________________________________________________________________
|
||||
| ... | inode | xattrs | extents | data inline | ... | inode ...
|
||||
|________|_______|(optional)|(optional)|__(optional)_|_____|__________
|
||||
|-> aligned with the inode slot size
|
||||
. .
|
||||
. .
|
||||
. .
|
||||
. .
|
||||
. .
|
||||
. .
|
||||
.____________________________________________________|-> aligned with 4B
|
||||
| xattr_ibody_header | shared xattrs | inline xattrs |
|
||||
|____________________|_______________|_______________|
|
||||
|-> 12 bytes <-|->x * 4 bytes<-| .
|
||||
. . .
|
||||
. . .
|
||||
. . .
|
||||
._______________________________.______________________.
|
||||
| id | id | id | id | ... | id | ent | ... | ent| ... |
|
||||
|____|____|____|____|______|____|_____|_____|____|_____|
|
||||
|-> aligned with 4B
|
||||
|-> aligned with 4B
|
||||
|
||||
Inode could be 32 or 64 bytes, which can be distinguished from a common
|
||||
field which all inode versions have -- i_advise:
|
||||
|
||||
__________________ __________________
|
||||
| i_advise | | i_advise |
|
||||
|__________________| |__________________|
|
||||
| ... | | ... |
|
||||
| | | |
|
||||
|__________________| 32 bytes | |
|
||||
| |
|
||||
|__________________| 64 bytes
|
||||
|
||||
Xattrs, extents, data inline are followed by the corresponding inode with
|
||||
proper alignes, and they could be optional for different data mappings,
|
||||
_currently_ there are totally 3 valid data mappings supported:
|
||||
|
||||
1) flat file data without data inline (no extent);
|
||||
2) fixed-output size data compression (must have extents);
|
||||
3) flat file data with tail-end data inline (no extent);
|
||||
|
||||
The size of the optional xattrs is indicated by i_xattr_count in inode
|
||||
header. Large xattrs or xattrs shared by many different files can be
|
||||
stored in shared xattrs metadata rather than inlined right after inode.
|
||||
|
||||
2. Shared xattrs metadata space
|
||||
Shared xattrs space is similar to the above inode space, started with
|
||||
a specific block indicated by xattr_blkaddr, organized one by one with
|
||||
proper align.
|
||||
|
||||
Each share xattr can also be directly found by the following formula:
|
||||
xattr offset = xattr_blkaddr * block_size + 4 * xattr_id
|
||||
|
||||
|-> aligned by 4 bytes
|
||||
+ xattr_blkaddr blocks |-> aligned with 4 bytes
|
||||
_________________________________________________________________________
|
||||
| ... | xattr_entry | xattr data | ... | xattr_entry | xattr data ...
|
||||
|________|_____________|_____________|_____|______________|_______________
|
||||
|
||||
Directories
|
||||
-----------
|
||||
All directories are now organized in a compact on-disk format. Note that
|
||||
each directory block is divided into index and name areas in order to support
|
||||
random file lookup, and all directory entries are _strictly_ recorded in
|
||||
alphabetical order in order to support improved prefix binary search
|
||||
algorithm (could refer to the related source code).
|
||||
|
||||
___________________________
|
||||
/ |
|
||||
/ ______________|________________
|
||||
/ / | nameoff1 | nameoffN-1
|
||||
____________.______________._______________v________________v__________
|
||||
| dirent | dirent | ... | dirent | filename | filename | ... | filename |
|
||||
|___.0___|____1___|_____|___N-1__|____0_____|____1_____|_____|___N-1____|
|
||||
\ ^
|
||||
\ | * could have
|
||||
\ | trailing '\0'
|
||||
\________________________| nameoff0
|
||||
|
||||
Directory block
|
||||
|
||||
Note that apart from the offset of the first filename, nameoff0 also indicates
|
||||
the total number of directory entries in this block since it is no need to
|
||||
introduce another on-disk field at all.
|
||||
|
||||
Compression
|
||||
-----------
|
||||
Currently, EROFS supports 4KB fixed-output clustersize transparent file
|
||||
compression, as illustrated below:
|
||||
|
||||
|---- Variant-Length Extent ----|-------- VLE --------|----- VLE -----
|
||||
clusterofs clusterofs clusterofs
|
||||
| | | logical data
|
||||
_________v_______________________________v_____________________v_______________
|
||||
... | . | | . | | . | ...
|
||||
____|____.________|_____________|________.____|_____________|__.__________|____
|
||||
|-> cluster <-|-> cluster <-|-> cluster <-|-> cluster <-|-> cluster <-|
|
||||
size size size size size
|
||||
. . . .
|
||||
. . . .
|
||||
. . . .
|
||||
_______._____________._____________._____________._____________________
|
||||
... | | | | ... physical data
|
||||
_______|_____________|_____________|_____________|_____________________
|
||||
|-> cluster <-|-> cluster <-|-> cluster <-|
|
||||
size size size
|
||||
|
||||
Currently each on-disk physical cluster can contain 4KB (un)compressed data
|
||||
at most. For each logical cluster, there is a corresponding on-disk index to
|
||||
describe its cluster type, physical cluster address, etc.
|
||||
|
||||
See "struct z_erofs_vle_decompressed_index" in erofs_fs.h for more details.
|
||||
|
||||
+10
-7
@@ -5488,6 +5488,16 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kristoffer/linux-hpc.git
|
||||
F: drivers/video/fbdev/s1d13xxxfb.c
|
||||
F: include/video/s1d13xxxfb.h
|
||||
|
||||
EROFS FILE SYSTEM
|
||||
M: Gao Xiang <xiang@kernel.org>
|
||||
M: Chao Yu <yuchao0@huawei.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/xiang/erofs.git
|
||||
F: Documentation/filesystems/erofs.txt
|
||||
F: fs/erofs/
|
||||
F: include/trace/events/erofs.h
|
||||
|
||||
ERRSEQ ERROR TRACKING INFRASTRUCTURE
|
||||
M: Jeff Layton <jlayton@kernel.org>
|
||||
S: Maintained
|
||||
@@ -13829,13 +13839,6 @@ M: H Hartley Sweeten <hsweeten@visionengravers.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/staging/comedi/
|
||||
|
||||
STAGING - EROFS FILE SYSTEM
|
||||
M: Gao Xiang <gaoxiang25@huawei.com>
|
||||
M: Chao Yu <yuchao0@huawei.com>
|
||||
L: linux-erofs@lists.ozlabs.org
|
||||
S: Maintained
|
||||
F: drivers/staging/erofs/
|
||||
|
||||
STAGING - FLARION FT1000 DRIVERS
|
||||
M: Marek Belisko <marek.belisko@gmail.com>
|
||||
S: Odd Fixes
|
||||
|
||||
@@ -438,6 +438,9 @@ CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_PSTORE=y
|
||||
CONFIG_PSTORE_CONSOLE=y
|
||||
CONFIG_PSTORE_RAM=y
|
||||
CONFIG_EROFS_FS=y
|
||||
CONFIG_EROFS_FS_PCPU_KTHREAD=y
|
||||
CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_CODEPAGE_737=y
|
||||
CONFIG_NLS_CODEPAGE_775=y
|
||||
|
||||
@@ -127,10 +127,10 @@ static unsigned long f_extend(unsigned long address)
|
||||
#ifdef CONFIG_64BIT
|
||||
if(unlikely(parisc_narrow_firmware)) {
|
||||
if((address & 0xff000000) == 0xf0000000)
|
||||
return 0xf0f0f0f000000000UL | (u32)address;
|
||||
return (0xfffffff0UL << 32) | (u32)address;
|
||||
|
||||
if((address & 0xf0000000) == 0xf0000000)
|
||||
return 0xffffffff00000000UL | (u32)address;
|
||||
return (0xffffffffUL << 32) | (u32)address;
|
||||
}
|
||||
#endif
|
||||
return address;
|
||||
|
||||
@@ -21,8 +21,8 @@ obj-$(CONFIG_PPC32) += div64.o copy_32.o crtsavres.o strlen_32.o
|
||||
# 64-bit linker creates .sfpr on demand for final link (vmlinux),
|
||||
# so it is only needed for modules, and only for older linkers which
|
||||
# do not support --save-restore-funcs
|
||||
ifeq ($(call ld-ifversion, -lt, 225000000, y),y)
|
||||
always-$(CONFIG_PPC64) += crtsavres.o
|
||||
ifeq ($(call ld-ifversion, -lt, 225000000, y)$(CONFIG_PPC64),yy)
|
||||
always += crtsavres.o
|
||||
endif
|
||||
|
||||
obj-$(CONFIG_PPC_BOOK3S_64) += copyuser_power7.o copypage_power7.o \
|
||||
|
||||
@@ -373,6 +373,9 @@ CONFIG_TMPFS_POSIX_ACL=y
|
||||
CONFIG_PSTORE=y
|
||||
CONFIG_PSTORE_CONSOLE=y
|
||||
CONFIG_PSTORE_RAM=y
|
||||
CONFIG_EROFS_FS=y
|
||||
CONFIG_EROFS_FS_PCPU_KTHREAD=y
|
||||
CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI=y
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_CODEPAGE_737=y
|
||||
CONFIG_NLS_CODEPAGE_775=y
|
||||
|
||||
@@ -271,15 +271,6 @@ static void init_amd_k6(struct cpuinfo_x86 *c)
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
/*
|
||||
* Work around Erratum 1386. The XSAVES instruction malfunctions in
|
||||
* certain circumstances on Zen1/2 uarch, and not all parts have had
|
||||
* updated microcode at the time of writing (March 2023).
|
||||
*
|
||||
* Affected parts all have no supervisor XSAVE states, meaning that
|
||||
* the XSAVEC instruction (which works fine) is equivalent.
|
||||
*/
|
||||
clear_cpu_cap(c, X86_FEATURE_XSAVES);
|
||||
}
|
||||
|
||||
static void init_amd_k7(struct cpuinfo_x86 *c)
|
||||
@@ -979,6 +970,17 @@ static void init_amd_zn(struct cpuinfo_x86 *c)
|
||||
if (c->x86 == 0x19 && !cpu_has(c, X86_FEATURE_BTC_NO))
|
||||
set_cpu_cap(c, X86_FEATURE_BTC_NO);
|
||||
}
|
||||
|
||||
/*
|
||||
* Work around Erratum 1386. The XSAVES instruction malfunctions in
|
||||
* certain circumstances on Zen1/2 uarch, and not all parts have had
|
||||
* updated microcode at the time of writing (March 2023).
|
||||
*
|
||||
* Affected parts all have no supervisor XSAVE states, meaning that
|
||||
* the XSAVEC instruction (which works fine) is equivalent.
|
||||
*/
|
||||
if (c->x86 == 0x17)
|
||||
clear_cpu_cap(c, X86_FEATURE_XSAVES);
|
||||
}
|
||||
|
||||
static bool cpu_has_zenbleed_microcode(void)
|
||||
|
||||
+1
-2
@@ -1596,7 +1596,6 @@ void bio_set_pages_dirty(struct bio *bio)
|
||||
int i;
|
||||
|
||||
bio_for_each_segment_all(bvec, bio, i) {
|
||||
if (!PageCompound(bvec->bv_page))
|
||||
set_page_dirty_lock(bvec->bv_page);
|
||||
}
|
||||
}
|
||||
@@ -1656,7 +1655,7 @@ void bio_check_pages_dirty(struct bio *bio)
|
||||
int i;
|
||||
|
||||
bio_for_each_segment_all(bvec, bio, i) {
|
||||
if (!PageDirty(bvec->bv_page) && !PageCompound(bvec->bv_page))
|
||||
if (!PageDirty(bvec->bv_page))
|
||||
goto defer;
|
||||
}
|
||||
|
||||
|
||||
@@ -236,6 +236,7 @@ static struct crypto_larval *__crypto_register_alg(struct crypto_alg *alg)
|
||||
}
|
||||
|
||||
if (!strcmp(q->cra_driver_name, alg->cra_name) ||
|
||||
!strcmp(q->cra_driver_name, alg->cra_driver_name) ||
|
||||
!strcmp(q->cra_name, alg->cra_driver_name))
|
||||
goto err;
|
||||
}
|
||||
|
||||
@@ -3805,6 +3805,50 @@ define_dev_printk_level(_dev_info, KERN_INFO);
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* dev_err_probe - probe error check and log helper
|
||||
* @dev: the pointer to the struct device
|
||||
* @err: error value to test
|
||||
* @fmt: printf-style format string
|
||||
* @...: arguments as specified in the format string
|
||||
*
|
||||
* This helper implements common pattern present in probe functions for error
|
||||
* checking: print debug or error message depending if the error value is
|
||||
* -EPROBE_DEFER and propagate error upwards.
|
||||
* It replaces code sequence::
|
||||
* if (err != -EPROBE_DEFER)
|
||||
* dev_err(dev, ...);
|
||||
* else
|
||||
* dev_dbg(dev, ...);
|
||||
* return err;
|
||||
*
|
||||
* with::
|
||||
*
|
||||
* return dev_err_probe(dev, err, ...);
|
||||
*
|
||||
* Returns @err.
|
||||
*
|
||||
*/
|
||||
int dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
|
||||
{
|
||||
struct va_format vaf;
|
||||
va_list args;
|
||||
|
||||
va_start(args, fmt);
|
||||
vaf.fmt = fmt;
|
||||
vaf.va = &args;
|
||||
|
||||
if (err != -EPROBE_DEFER)
|
||||
dev_err(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
else
|
||||
dev_dbg(dev, "error %pe: %pV", ERR_PTR(err), &vaf);
|
||||
|
||||
va_end(args);
|
||||
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_err_probe);
|
||||
|
||||
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
|
||||
{
|
||||
return fwnode && !IS_ERR(fwnode->secondary);
|
||||
|
||||
@@ -24,10 +24,13 @@
|
||||
#include <linux/random.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#define RNG_MODULE_NAME "hw_random"
|
||||
|
||||
#define RNG_BUFFER_SIZE (SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES)
|
||||
|
||||
static struct hwrng *current_rng;
|
||||
/* the current rng has been explicitly chosen by user via sysfs */
|
||||
static int cur_rng_set_by_user;
|
||||
@@ -59,7 +62,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
|
||||
|
||||
static size_t rng_buffer_size(void)
|
||||
{
|
||||
return SMP_CACHE_BYTES < 32 ? 32 : SMP_CACHE_BYTES;
|
||||
return RNG_BUFFER_SIZE;
|
||||
}
|
||||
|
||||
static void add_early_randomness(struct hwrng *rng)
|
||||
@@ -202,6 +205,7 @@ static inline int rng_get_data(struct hwrng *rng, u8 *buffer, size_t size,
|
||||
static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
size_t size, loff_t *offp)
|
||||
{
|
||||
u8 buffer[RNG_BUFFER_SIZE];
|
||||
ssize_t ret = 0;
|
||||
int err = 0;
|
||||
int bytes_read, len;
|
||||
@@ -229,34 +233,37 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
if (bytes_read < 0) {
|
||||
err = bytes_read;
|
||||
goto out_unlock_reading;
|
||||
}
|
||||
data_avail = bytes_read;
|
||||
}
|
||||
|
||||
if (!data_avail) {
|
||||
if (filp->f_flags & O_NONBLOCK) {
|
||||
} else if (bytes_read == 0 &&
|
||||
(filp->f_flags & O_NONBLOCK)) {
|
||||
err = -EAGAIN;
|
||||
goto out_unlock_reading;
|
||||
}
|
||||
} else {
|
||||
|
||||
data_avail = bytes_read;
|
||||
}
|
||||
|
||||
len = data_avail;
|
||||
if (len) {
|
||||
if (len > size)
|
||||
len = size;
|
||||
|
||||
data_avail -= len;
|
||||
|
||||
if (copy_to_user(buf + ret, rng_buffer + data_avail,
|
||||
len)) {
|
||||
memcpy(buffer, rng_buffer + data_avail, len);
|
||||
}
|
||||
mutex_unlock(&reading_mutex);
|
||||
put_rng(rng);
|
||||
|
||||
if (len) {
|
||||
if (copy_to_user(buf + ret, buffer, len)) {
|
||||
err = -EFAULT;
|
||||
goto out_unlock_reading;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size -= len;
|
||||
ret += len;
|
||||
}
|
||||
|
||||
mutex_unlock(&reading_mutex);
|
||||
put_rng(rng);
|
||||
|
||||
if (need_resched())
|
||||
schedule_timeout_interruptible(1);
|
||||
@@ -267,6 +274,7 @@ static ssize_t rng_dev_read(struct file *filp, char __user *buf,
|
||||
}
|
||||
}
|
||||
out:
|
||||
memzero_explicit(buffer, sizeof(buffer));
|
||||
return ret ? : err;
|
||||
|
||||
out_unlock_reading:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Copyright (c) 2016-2017, 2019-2021, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
@@ -2679,6 +2680,7 @@ static struct clk_regmap *gcc_660_clocks[] = {
|
||||
static const struct qcom_reset_map gcc_660_resets[] = {
|
||||
[GCC_QUSB2PHY_PRIM_BCR] = { 0x12000 },
|
||||
[GCC_QUSB2PHY_SEC_BCR] = { 0x12004 },
|
||||
[GCC_SDCC1_BCR] = { 0x16000 },
|
||||
[GCC_UFS_BCR] = { 0x75000 },
|
||||
[GCC_USB3_DP_PHY_BCR] = { 0x50028 },
|
||||
[GCC_USB3_PHY_BCR] = { 0x50020 },
|
||||
|
||||
@@ -107,6 +107,9 @@ nouveau_vma_new(struct nouveau_bo *nvbo, struct nouveau_vmm *vmm,
|
||||
} else {
|
||||
ret = nvif_vmm_get(&vmm->vmm, PTES, false, mem->mem.page, 0,
|
||||
mem->mem.size, &tmp);
|
||||
if (ret)
|
||||
goto done;
|
||||
|
||||
vma->addr = tmp.addr;
|
||||
}
|
||||
|
||||
|
||||
@@ -605,14 +605,20 @@ static void mtk_pcie_intr_handler(struct irq_desc *desc)
|
||||
if (status & MSI_STATUS){
|
||||
unsigned long imsi_status;
|
||||
|
||||
/*
|
||||
* The interrupt status can be cleared even if the
|
||||
* MSI status remains pending. As such, given the
|
||||
* edge-triggered interrupt type, its status should
|
||||
* be cleared before being dispatched to the
|
||||
* handler of the underlying device.
|
||||
*/
|
||||
writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
|
||||
while ((imsi_status = readl(port->base + PCIE_IMSI_STATUS))) {
|
||||
for_each_set_bit(bit, &imsi_status, MTK_MSI_IRQS_NUM) {
|
||||
virq = irq_find_mapping(port->inner_domain, bit);
|
||||
generic_handle_irq(virq);
|
||||
}
|
||||
}
|
||||
/* Clear MSI interrupt status */
|
||||
writel(MSI_STATUS, port->base + PCIE_INT_STATUS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -381,6 +381,7 @@ static void virtio_rpmsg_release_device(struct device *dev)
|
||||
struct rpmsg_device *rpdev = to_rpmsg_device(dev);
|
||||
struct virtio_rpmsg_channel *vch = to_virtio_rpmsg_channel(rpdev);
|
||||
|
||||
kfree(rpdev->driver_override);
|
||||
kfree(vch);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/units.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
||||
#define SC16IS7XX_NAME "sc16is7xx"
|
||||
@@ -1406,9 +1407,12 @@ static int sc16is7xx_spi_probe(struct spi_device *spi)
|
||||
|
||||
/* Setup SPI bus */
|
||||
spi->bits_per_word = 8;
|
||||
/* only supports mode 0 on SC16IS762 */
|
||||
/* For all variants, only mode 0 is supported */
|
||||
if ((spi->mode & SPI_MODE_X_MASK) != SPI_MODE_0)
|
||||
return dev_err_probe(&spi->dev, -EINVAL, "Unsupported SPI mode\n");
|
||||
|
||||
spi->mode = spi->mode ? : SPI_MODE_0;
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 15000000;
|
||||
spi->max_speed_hz = spi->max_speed_hz ? : 4 * HZ_PER_MHZ;
|
||||
ret = spi_setup(spi);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -76,3 +76,20 @@ config EROFS_FS_ZIP
|
||||
|
||||
If you don't want to enable compression feature, say N.
|
||||
|
||||
config EROFS_FS_PCPU_KTHREAD
|
||||
bool "EROFS per-cpu decompression kthread workers"
|
||||
depends on EROFS_FS_ZIP
|
||||
help
|
||||
Saying Y here enables per-CPU kthread workers pool to carry out
|
||||
async decompression for low latencies on some architectures.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config EROFS_FS_PCPU_KTHREAD_HIPRI
|
||||
bool "EROFS high priority per-CPU kthread workers"
|
||||
depends on EROFS_FS_ZIP && EROFS_FS_PCPU_KTHREAD
|
||||
help
|
||||
This permits EROFS to configure per-CPU kthread workers to run
|
||||
at higher priority.
|
||||
|
||||
If unsure, say N.
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
EROFS is still working in progress, thus it is not suitable
|
||||
for all productive uses. play at your own risk :)
|
||||
|
||||
TODO List:
|
||||
- add the missing error handling code
|
||||
(mainly existed in xattr and decompression submodules);
|
||||
|
||||
- finalize erofs ondisk format design (which means that
|
||||
minor on-disk revisions could happen later);
|
||||
|
||||
- documentation and detailed technical analysis;
|
||||
|
||||
- general code review and clean up
|
||||
(including confusing variable names and code snippets);
|
||||
|
||||
- support larger compressed clustersizes for selection
|
||||
(currently erofs only works as expected with the page-sized
|
||||
compressed cluster configuration, usually 4KB);
|
||||
|
||||
- support more lossless data compression algorithms
|
||||
in addition to LZ4 algorithms in VLE approach;
|
||||
|
||||
- data deduplication and other useful features.
|
||||
|
||||
erofs-mkfs (preview version) binaries for i386 / x86_64 are available at:
|
||||
|
||||
https://github.com/hsiangkao/erofs_mkfs_binary
|
||||
|
||||
It is still in progress opening mkfs source code to public,
|
||||
in any case an open-source mkfs will be released in the near future.
|
||||
|
||||
|
||||
Code, suggestions, etc, are welcome. Please feel free to
|
||||
ask and send patches,
|
||||
|
||||
To:
|
||||
linux-erofs mailing list <linux-erofs@lists.ozlabs.org>
|
||||
Gao Xiang <gaoxiang25@huawei.com>
|
||||
Chao Yu <yuchao0@huawei.com>
|
||||
|
||||
Cc: (for linux-kernel upstream patches)
|
||||
Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
linux-staging mailing list <devel@driverdev.osuosl.org>
|
||||
|
||||
+1
-1
@@ -208,7 +208,7 @@ submit_bio_retry:
|
||||
bio_set_dev(bio, sb->s_bdev);
|
||||
bio->bi_iter.bi_sector = (sector_t)blknr <<
|
||||
LOG_SECTORS_PER_BLOCK;
|
||||
bio->bi_opf = REQ_OP_READ | (ra ? REQ_RAHEAD : 0);
|
||||
bio->bi_opf = REQ_OP_READ;
|
||||
}
|
||||
|
||||
err = bio_add_page(bio, page, PAGE_SIZE, 0);
|
||||
|
||||
+104
-79
@@ -23,8 +23,7 @@ struct z_erofs_decompressor {
|
||||
*/
|
||||
int (*prepare_destpages)(struct z_erofs_decompress_req *rq,
|
||||
struct list_head *pagepool);
|
||||
int (*decompress)(struct z_erofs_decompress_req *rq, u8 *out,
|
||||
u8 *obase);
|
||||
int (*decompress)(struct z_erofs_decompress_req *rq, u8 *dst);
|
||||
char *name;
|
||||
};
|
||||
|
||||
@@ -124,48 +123,85 @@ static int z_erofs_lz4_prepare_destpages(struct z_erofs_decompress_req *rq,
|
||||
return kaddr ? 1 : 0;
|
||||
}
|
||||
|
||||
static void *generic_copy_inplace_data(struct z_erofs_decompress_req *rq,
|
||||
u8 *src, unsigned int pageofs_in)
|
||||
static void *z_erofs_handle_inplace_io(struct z_erofs_decompress_req *rq,
|
||||
void *inpage, void *out, unsigned int *inputmargin,
|
||||
int *maptype, bool support_0padding)
|
||||
{
|
||||
/*
|
||||
* if in-place decompression is ongoing, those decompressed
|
||||
* pages should be copied in order to avoid being overlapped.
|
||||
*/
|
||||
struct page **in = rq->in;
|
||||
u8 *const tmp = erofs_get_pcpubuf(1);
|
||||
u8 *tmpp = tmp;
|
||||
unsigned int inlen = rq->inputsize - pageofs_in;
|
||||
unsigned int count = min_t(uint, inlen, PAGE_SIZE - pageofs_in);
|
||||
unsigned int nrpages_in, nrpages_out;
|
||||
unsigned int ofull, oend, inputsize, total, i;
|
||||
struct page **in;
|
||||
void *src, *tmp;
|
||||
|
||||
while (tmpp < tmp + inlen) {
|
||||
if (!src)
|
||||
src = kmap_atomic(*in);
|
||||
memcpy(tmpp, src + pageofs_in, count);
|
||||
kunmap_atomic(src);
|
||||
src = NULL;
|
||||
tmpp += count;
|
||||
pageofs_in = 0;
|
||||
count = PAGE_SIZE;
|
||||
++in;
|
||||
inputsize = rq->inputsize;
|
||||
nrpages_in = PAGE_ALIGN(inputsize) >> PAGE_SHIFT;
|
||||
oend = rq->pageofs_out + rq->outputsize;
|
||||
ofull = PAGE_ALIGN(oend);
|
||||
nrpages_out = ofull >> PAGE_SHIFT;
|
||||
|
||||
if (rq->inplace_io) {
|
||||
if (rq->partial_decoding || !support_0padding ||
|
||||
ofull - oend < LZ4_DECOMPRESS_INPLACE_MARGIN(inputsize))
|
||||
goto docopy;
|
||||
|
||||
for (i = 0; i < nrpages_in; ++i)
|
||||
if (rq->out[nrpages_out - nrpages_in + i] !=
|
||||
rq->in[i])
|
||||
goto docopy;
|
||||
kunmap_atomic(inpage);
|
||||
*maptype = 3;
|
||||
return out + ((nrpages_out - nrpages_in) << PAGE_SHIFT);
|
||||
}
|
||||
return tmp;
|
||||
|
||||
if (nrpages_in <= 1) {
|
||||
*maptype = 0;
|
||||
return inpage;
|
||||
}
|
||||
kunmap_atomic(inpage);
|
||||
src = erofs_vm_map_ram(rq->in, nrpages_in);
|
||||
if (!src)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
*maptype = 1;
|
||||
return src;
|
||||
|
||||
docopy:
|
||||
/* Or copy compressed data which can be overlapped to per-CPU buffer */
|
||||
in = rq->in;
|
||||
src = erofs_get_pcpubuf(nrpages_in);
|
||||
if (!src) {
|
||||
DBG_BUGON(1);
|
||||
kunmap_atomic(inpage);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
|
||||
tmp = src;
|
||||
total = rq->inputsize;
|
||||
while (total) {
|
||||
unsigned int page_copycnt =
|
||||
min_t(unsigned int, total, PAGE_SIZE - *inputmargin);
|
||||
|
||||
if (!inpage)
|
||||
inpage = kmap_atomic(*in);
|
||||
memcpy(tmp, inpage + *inputmargin, page_copycnt);
|
||||
kunmap_atomic(inpage);
|
||||
inpage = NULL;
|
||||
tmp += page_copycnt;
|
||||
total -= page_copycnt;
|
||||
++in;
|
||||
*inputmargin = 0;
|
||||
}
|
||||
*maptype = 2;
|
||||
return src;
|
||||
}
|
||||
|
||||
static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out,
|
||||
u8 *obase)
|
||||
static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *dst)
|
||||
{
|
||||
const uint nrpages_out = PAGE_ALIGN(rq->pageofs_out +
|
||||
rq->outputsize) >> PAGE_SHIFT;
|
||||
unsigned int inputmargin, inlen;
|
||||
u8 *src, *src2;
|
||||
bool copied, support_0padding;
|
||||
int ret;
|
||||
unsigned int inputmargin;
|
||||
u8 *out, *headpage, *src;
|
||||
bool support_0padding;
|
||||
int ret, maptype;
|
||||
|
||||
if (rq->inputsize > PAGE_SIZE)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
src = kmap_atomic(*rq->in);
|
||||
src2 = src;
|
||||
DBG_BUGON(*rq->in == NULL);
|
||||
headpage = kmap_atomic(*rq->in);
|
||||
inputmargin = 0;
|
||||
support_0padding = false;
|
||||
|
||||
@@ -173,48 +209,37 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out,
|
||||
if (erofs_sb_has_lz4_0padding(EROFS_SB(rq->sb))) {
|
||||
support_0padding = true;
|
||||
|
||||
while (!src[inputmargin & ~PAGE_MASK])
|
||||
while (!headpage[inputmargin & ~PAGE_MASK])
|
||||
if (!(++inputmargin & ~PAGE_MASK))
|
||||
break;
|
||||
|
||||
if (inputmargin >= rq->inputsize) {
|
||||
kunmap_atomic(src);
|
||||
kunmap_atomic(headpage);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
copied = false;
|
||||
inlen = rq->inputsize - inputmargin;
|
||||
if (rq->inplace_io) {
|
||||
const uint oend = (rq->pageofs_out +
|
||||
rq->outputsize) & ~PAGE_MASK;
|
||||
if (rq->partial_decoding || !support_0padding ||
|
||||
rq->out[nrpages_out - 1] != rq->in[0] ||
|
||||
rq->inputsize - oend <
|
||||
LZ4_DECOMPRESS_INPLACE_MARGIN(inlen)) {
|
||||
src = generic_copy_inplace_data(rq, src, inputmargin);
|
||||
inputmargin = 0;
|
||||
copied = true;
|
||||
} else {
|
||||
src = obase + ((nrpages_out - 1) << PAGE_SHIFT);
|
||||
}
|
||||
}
|
||||
rq->inputsize -= inputmargin;
|
||||
src = z_erofs_handle_inplace_io(rq, headpage, dst, &inputmargin,
|
||||
&maptype, support_0padding);
|
||||
if (IS_ERR(src))
|
||||
return PTR_ERR(src);
|
||||
|
||||
out = dst + rq->pageofs_out;
|
||||
/* legacy format could compress extra data in a pcluster. */
|
||||
if (rq->partial_decoding || !support_0padding)
|
||||
ret = LZ4_decompress_safe_partial(src + inputmargin, out,
|
||||
inlen, rq->outputsize,
|
||||
rq->outputsize);
|
||||
rq->inputsize, rq->outputsize, rq->outputsize);
|
||||
else
|
||||
ret = LZ4_decompress_safe(src + inputmargin, out,
|
||||
inlen, rq->outputsize);
|
||||
rq->inputsize, rq->outputsize);
|
||||
|
||||
if (ret != rq->outputsize) {
|
||||
erofs_err(rq->sb, "failed to decompress %d in[%u, %u] out[%u]",
|
||||
ret, inlen, inputmargin, rq->outputsize);
|
||||
ret, rq->inputsize, inputmargin, rq->outputsize);
|
||||
|
||||
print_hex_dump(KERN_DEBUG, "[ in]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, src + inputmargin, inlen, true);
|
||||
16, 1, src + inputmargin, rq->inputsize, true);
|
||||
print_hex_dump(KERN_DEBUG, "[out]: ", DUMP_PREFIX_OFFSET,
|
||||
16, 1, out, rq->outputsize, true);
|
||||
|
||||
@@ -223,10 +248,16 @@ static int z_erofs_lz4_decompress(struct z_erofs_decompress_req *rq, u8 *out,
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
if (copied)
|
||||
if (maptype == 0) {
|
||||
kunmap_atomic(src);
|
||||
} else if (maptype == 1) {
|
||||
vm_unmap_ram(src, PAGE_ALIGN(rq->inputsize) >> PAGE_SHIFT);
|
||||
} else if (maptype == 2) {
|
||||
erofs_put_pcpubuf(src);
|
||||
else
|
||||
kunmap_atomic(src2);
|
||||
} else if (maptype != 3) {
|
||||
DBG_BUGON(1);
|
||||
return -EFAULT;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -276,8 +307,10 @@ static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq,
|
||||
const struct z_erofs_decompressor *alg = decompressors + rq->alg;
|
||||
unsigned int dst_maptype;
|
||||
void *dst;
|
||||
int ret, i;
|
||||
int ret;
|
||||
|
||||
/* two optimized fast paths only for non bigpcluster cases yet */
|
||||
if (rq->inputsize <= PAGE_SIZE) {
|
||||
if (nrpages_out == 1 && !rq->inplace_io) {
|
||||
DBG_BUGON(!*rq->out);
|
||||
dst = kmap_atomic(*rq->out);
|
||||
@@ -296,7 +329,7 @@ static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq,
|
||||
return PTR_ERR(dst);
|
||||
|
||||
rq->inplace_io = false;
|
||||
ret = alg->decompress(rq, dst, NULL);
|
||||
ret = alg->decompress(rq, dst);
|
||||
if (!ret)
|
||||
copy_from_pcpubuf(rq->out, dst, rq->pageofs_out,
|
||||
rq->outputsize);
|
||||
@@ -304,33 +337,25 @@ static int z_erofs_decompress_generic(struct z_erofs_decompress_req *rq,
|
||||
erofs_put_pcpubuf(dst);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
/* general decoding path which can be used for all cases */
|
||||
ret = alg->prepare_destpages(rq, pagepool);
|
||||
if (ret < 0) {
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (ret) {
|
||||
if (ret) {
|
||||
dst = page_address(*rq->out);
|
||||
dst_maptype = 1;
|
||||
goto dstmap_out;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
while (1) {
|
||||
dst = vm_map_ram(rq->out, nrpages_out, -1, PAGE_KERNEL);
|
||||
|
||||
/* retry two more times (totally 3 times) */
|
||||
if (dst || ++i >= 3)
|
||||
break;
|
||||
vm_unmap_aliases();
|
||||
}
|
||||
|
||||
dst = erofs_vm_map_ram(rq->out, nrpages_out);
|
||||
if (!dst)
|
||||
return -ENOMEM;
|
||||
|
||||
dst_maptype = 2;
|
||||
|
||||
dstmap_out:
|
||||
ret = alg->decompress(rq, dst + rq->pageofs_out, dst);
|
||||
ret = alg->decompress(rq, dst);
|
||||
|
||||
if (!dst_maptype)
|
||||
kunmap_atomic(dst);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#ifndef __EROFS_FS_H
|
||||
#define __EROFS_FS_H
|
||||
|
||||
#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2
|
||||
#define EROFS_SUPER_OFFSET 1024
|
||||
|
||||
#define EROFS_FEATURE_COMPAT_SB_CHKSUM 0x00000001
|
||||
|
||||
@@ -396,6 +396,21 @@ int erofs_namei(struct inode *dir, struct qstr *name,
|
||||
/* dir.c */
|
||||
extern const struct file_operations erofs_dir_fops;
|
||||
|
||||
static inline void *erofs_vm_map_ram(struct page **pages, unsigned int count)
|
||||
{
|
||||
int retried = 0;
|
||||
|
||||
while (1) {
|
||||
void *p = vm_map_ram(pages, count, -1, PAGE_KERNEL);
|
||||
|
||||
/* retry two more times (totally 3 times) */
|
||||
if (p || ++retried >= 3)
|
||||
return p;
|
||||
vm_unmap_aliases();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* pcpubuf.c */
|
||||
void *erofs_get_pcpubuf(unsigned int requiredpages);
|
||||
void erofs_put_pcpubuf(void *ptr);
|
||||
|
||||
+184
-46
@@ -6,7 +6,7 @@
|
||||
#include "zdata.h"
|
||||
#include "compress.h"
|
||||
#include <linux/prefetch.h>
|
||||
|
||||
#include <linux/cpuhotplug.h>
|
||||
#include <trace/events/erofs.h>
|
||||
|
||||
/*
|
||||
@@ -124,24 +124,128 @@ typedef tagptr1_t compressed_page_t;
|
||||
|
||||
static struct workqueue_struct *z_erofs_workqueue __read_mostly;
|
||||
|
||||
void z_erofs_exit_zip_subsystem(void)
|
||||
#ifdef CONFIG_EROFS_FS_PCPU_KTHREAD
|
||||
static struct kthread_worker __rcu **z_erofs_pcpu_workers;
|
||||
|
||||
static void erofs_destroy_percpu_workers(void)
|
||||
{
|
||||
destroy_workqueue(z_erofs_workqueue);
|
||||
z_erofs_destroy_pcluster_pool();
|
||||
struct kthread_worker *worker;
|
||||
unsigned int cpu;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
worker = rcu_dereference_protected(
|
||||
z_erofs_pcpu_workers[cpu], 1);
|
||||
rcu_assign_pointer(z_erofs_pcpu_workers[cpu], NULL);
|
||||
if (worker)
|
||||
kthread_destroy_worker(worker);
|
||||
}
|
||||
kfree(z_erofs_pcpu_workers);
|
||||
}
|
||||
|
||||
static inline int z_erofs_init_workqueue(void)
|
||||
static struct kthread_worker *erofs_init_percpu_worker(int cpu)
|
||||
{
|
||||
const unsigned int onlinecpus = num_possible_cpus();
|
||||
struct kthread_worker *worker =
|
||||
kthread_create_worker_on_cpu(cpu, 0, "erofs_worker/%u", cpu);
|
||||
|
||||
/*
|
||||
* no need to spawn too many threads, limiting threads could minimum
|
||||
* scheduling overhead, perhaps per-CPU threads should be better?
|
||||
*/
|
||||
z_erofs_workqueue = alloc_workqueue("erofs_unzipd",
|
||||
WQ_UNBOUND | WQ_HIGHPRI,
|
||||
onlinecpus + onlinecpus / 4);
|
||||
return z_erofs_workqueue ? 0 : -ENOMEM;
|
||||
if (IS_ERR(worker))
|
||||
return worker;
|
||||
if (IS_ENABLED(CONFIG_EROFS_FS_PCPU_KTHREAD_HIPRI))
|
||||
sched_set_fifo_low(worker->task);
|
||||
else
|
||||
sched_set_normal(worker->task, 0);
|
||||
return worker;
|
||||
}
|
||||
|
||||
static int erofs_init_percpu_workers(void)
|
||||
{
|
||||
struct kthread_worker *worker;
|
||||
unsigned int cpu;
|
||||
|
||||
z_erofs_pcpu_workers = kcalloc(num_possible_cpus(),
|
||||
sizeof(struct kthread_worker *), GFP_ATOMIC);
|
||||
if (!z_erofs_pcpu_workers)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_online_cpu(cpu) { /* could miss cpu{off,on}line? */
|
||||
worker = erofs_init_percpu_worker(cpu);
|
||||
if (!IS_ERR(worker))
|
||||
rcu_assign_pointer(z_erofs_pcpu_workers[cpu], worker);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline void erofs_destroy_percpu_workers(void) {}
|
||||
static inline int erofs_init_percpu_workers(void) { return 0; }
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_HOTPLUG_CPU) && defined(CONFIG_EROFS_FS_PCPU_KTHREAD)
|
||||
static DEFINE_SPINLOCK(z_erofs_pcpu_worker_lock);
|
||||
static enum cpuhp_state erofs_cpuhp_state;
|
||||
|
||||
static int erofs_cpu_online(unsigned int cpu)
|
||||
{
|
||||
struct kthread_worker *worker, *old;
|
||||
|
||||
worker = erofs_init_percpu_worker(cpu);
|
||||
if (IS_ERR(worker))
|
||||
return PTR_ERR(worker);
|
||||
|
||||
spin_lock(&z_erofs_pcpu_worker_lock);
|
||||
old = rcu_dereference_protected(z_erofs_pcpu_workers[cpu],
|
||||
lockdep_is_held(&z_erofs_pcpu_worker_lock));
|
||||
if (!old)
|
||||
rcu_assign_pointer(z_erofs_pcpu_workers[cpu], worker);
|
||||
spin_unlock(&z_erofs_pcpu_worker_lock);
|
||||
if (old)
|
||||
kthread_destroy_worker(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erofs_cpu_offline(unsigned int cpu)
|
||||
{
|
||||
struct kthread_worker *worker;
|
||||
|
||||
spin_lock(&z_erofs_pcpu_worker_lock);
|
||||
worker = rcu_dereference_protected(z_erofs_pcpu_workers[cpu],
|
||||
lockdep_is_held(&z_erofs_pcpu_worker_lock));
|
||||
rcu_assign_pointer(z_erofs_pcpu_workers[cpu], NULL);
|
||||
spin_unlock(&z_erofs_pcpu_worker_lock);
|
||||
|
||||
synchronize_rcu();
|
||||
if (worker)
|
||||
kthread_destroy_worker(worker);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int erofs_cpu_hotplug_init(void)
|
||||
{
|
||||
int state;
|
||||
|
||||
state = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
|
||||
"fs/erofs:online", erofs_cpu_online, erofs_cpu_offline);
|
||||
if (state < 0)
|
||||
return state;
|
||||
|
||||
erofs_cpuhp_state = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void erofs_cpu_hotplug_destroy(void)
|
||||
{
|
||||
if (erofs_cpuhp_state)
|
||||
cpuhp_remove_state_nocalls(erofs_cpuhp_state);
|
||||
}
|
||||
#else /* !CONFIG_HOTPLUG_CPU || !CONFIG_EROFS_FS_PCPU_KTHREAD */
|
||||
static inline int erofs_cpu_hotplug_init(void) { return 0; }
|
||||
static inline void erofs_cpu_hotplug_destroy(void) {}
|
||||
#endif
|
||||
|
||||
void z_erofs_exit_zip_subsystem(void)
|
||||
{
|
||||
erofs_cpu_hotplug_destroy();
|
||||
erofs_destroy_percpu_workers();
|
||||
destroy_workqueue(z_erofs_workqueue);
|
||||
z_erofs_destroy_pcluster_pool();
|
||||
}
|
||||
|
||||
int __init z_erofs_init_zip_subsystem(void)
|
||||
@@ -149,10 +253,31 @@ int __init z_erofs_init_zip_subsystem(void)
|
||||
int err = z_erofs_create_pcluster_pool();
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
err = z_erofs_init_workqueue();
|
||||
goto out_error_pcluster_pool;
|
||||
|
||||
z_erofs_workqueue = alloc_workqueue("erofs_worker",
|
||||
WQ_UNBOUND | WQ_HIGHPRI, num_possible_cpus());
|
||||
if (!z_erofs_workqueue) {
|
||||
err = -ENOMEM;
|
||||
goto out_error_workqueue_init;
|
||||
}
|
||||
|
||||
err = erofs_init_percpu_workers();
|
||||
if (err)
|
||||
goto out_error_pcpu_worker;
|
||||
|
||||
err = erofs_cpu_hotplug_init();
|
||||
if (err < 0)
|
||||
goto out_error_cpuhp_init;
|
||||
return err;
|
||||
|
||||
out_error_cpuhp_init:
|
||||
erofs_destroy_percpu_workers();
|
||||
out_error_pcpu_worker:
|
||||
destroy_workqueue(z_erofs_workqueue);
|
||||
out_error_workqueue_init:
|
||||
z_erofs_destroy_pcluster_pool();
|
||||
out_error_pcluster_pool:
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -216,7 +341,6 @@ struct z_erofs_decompress_frontend {
|
||||
struct z_erofs_collector clt;
|
||||
struct erofs_map_blocks map;
|
||||
|
||||
bool readahead;
|
||||
/* used for applying cache strategy on the fly */
|
||||
bool backmost;
|
||||
erofs_off_t headoffset;
|
||||
@@ -331,11 +455,8 @@ int erofs_try_to_free_all_cached_pages(struct erofs_sb_info *sbi,
|
||||
|
||||
/* barrier is implied in the following 'unlock_page' */
|
||||
WRITE_ONCE(pcl->compressed_pages[i], NULL);
|
||||
|
||||
set_page_private(page, 0);
|
||||
ClearPagePrivate(page);
|
||||
detach_page_private(page);
|
||||
unlock_page(page);
|
||||
put_page(page);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -358,11 +479,8 @@ int erofs_try_to_free_cached_page(struct address_space *mapping,
|
||||
}
|
||||
erofs_workgroup_unfreeze(&pcl->obj, 1);
|
||||
|
||||
if (ret) {
|
||||
set_page_private(page, 0);
|
||||
ClearPagePrivate(page);
|
||||
put_page(page);
|
||||
}
|
||||
if (ret)
|
||||
detach_page_private(page);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -779,6 +897,12 @@ err_out:
|
||||
}
|
||||
|
||||
static void z_erofs_decompressqueue_work(struct work_struct *work);
|
||||
#ifdef CONFIG_EROFS_FS_PCPU_KTHREAD
|
||||
static void z_erofs_decompressqueue_kthread_work(struct kthread_work *work)
|
||||
{
|
||||
z_erofs_decompressqueue_work((struct work_struct *)work);
|
||||
}
|
||||
#endif
|
||||
static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
bool sync, int bios)
|
||||
{
|
||||
@@ -786,12 +910,9 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
|
||||
/* wake up the caller thread for sync decompression */
|
||||
if (sync) {
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&io->u.wait.lock, flags);
|
||||
if (!atomic_add_return(bios, &io->pending_bios))
|
||||
wake_up_locked(&io->u.wait);
|
||||
spin_unlock_irqrestore(&io->u.wait.lock, flags);
|
||||
complete(&io->u.done);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -799,7 +920,22 @@ static void z_erofs_decompress_kickoff(struct z_erofs_decompressqueue *io,
|
||||
return;
|
||||
/* Use workqueue and sync decompression for atomic contexts only */
|
||||
if (in_atomic() || irqs_disabled()) {
|
||||
#ifdef CONFIG_EROFS_FS_PCPU_KTHREAD
|
||||
struct kthread_worker *worker;
|
||||
|
||||
rcu_read_lock();
|
||||
worker = rcu_dereference(
|
||||
z_erofs_pcpu_workers[raw_smp_processor_id()]);
|
||||
if (!worker) {
|
||||
INIT_WORK(&io->u.work, z_erofs_decompressqueue_work);
|
||||
queue_work(z_erofs_workqueue, &io->u.work);
|
||||
} else {
|
||||
kthread_queue_work(worker, &io->u.kthread_work);
|
||||
}
|
||||
rcu_read_unlock();
|
||||
#else
|
||||
queue_work(z_erofs_workqueue, &io->u.work);
|
||||
#endif
|
||||
sbi->readahead_sync_decompress = true;
|
||||
return;
|
||||
}
|
||||
@@ -1187,8 +1323,9 @@ out_tocache:
|
||||
set_page_private(page, Z_EROFS_SHORTLIVED_PAGE);
|
||||
goto out;
|
||||
}
|
||||
set_page_private(page, (unsigned long)pcl);
|
||||
SetPagePrivate(page);
|
||||
attach_page_private(page, pcl);
|
||||
/* drop a refcount added by allocpage (then we have 2 refs here) */
|
||||
put_page(page);
|
||||
|
||||
out: /* the only exit (for tracing and debugging) */
|
||||
return page;
|
||||
@@ -1206,11 +1343,16 @@ jobqueue_init(struct super_block *sb,
|
||||
*fg = true;
|
||||
goto fg_out;
|
||||
}
|
||||
#ifdef CONFIG_EROFS_FS_PCPU_KTHREAD
|
||||
kthread_init_work(&q->u.kthread_work,
|
||||
z_erofs_decompressqueue_kthread_work);
|
||||
#else
|
||||
INIT_WORK(&q->u.work, z_erofs_decompressqueue_work);
|
||||
#endif
|
||||
} else {
|
||||
fg_out:
|
||||
q = fgq;
|
||||
init_waitqueue_head(&fgq->u.wait);
|
||||
init_completion(&fgq->u.done);
|
||||
atomic_set(&fgq->pending_bios, 0);
|
||||
}
|
||||
q->sb = sb;
|
||||
@@ -1259,7 +1401,7 @@ static void move_to_bypass_jobqueue(struct z_erofs_pcluster *pcl,
|
||||
}
|
||||
|
||||
static void z_erofs_submit_queue(struct super_block *sb,
|
||||
struct z_erofs_decompress_frontend *f,
|
||||
z_erofs_next_pcluster_t owned_head,
|
||||
struct list_head *pagepool,
|
||||
struct z_erofs_decompressqueue *fgq,
|
||||
bool *force_fg)
|
||||
@@ -1268,7 +1410,6 @@ static void z_erofs_submit_queue(struct super_block *sb,
|
||||
z_erofs_next_pcluster_t qtail[NR_JOBQUEUES];
|
||||
struct z_erofs_decompressqueue *q[NR_JOBQUEUES];
|
||||
void *bi_private;
|
||||
z_erofs_next_pcluster_t owned_head = f->clt.owned_head;
|
||||
/* since bio will be NULL, no need to initialize last_index */
|
||||
pgoff_t last_index;
|
||||
unsigned int nr_bios = 0;
|
||||
@@ -1324,8 +1465,6 @@ submit_bio_retry:
|
||||
LOG_SECTORS_PER_BLOCK;
|
||||
bio->bi_private = bi_private;
|
||||
bio->bi_opf = REQ_OP_READ;
|
||||
if (f->readahead)
|
||||
bio->bi_opf |= REQ_RAHEAD;
|
||||
++nr_bios;
|
||||
}
|
||||
|
||||
@@ -1347,7 +1486,7 @@ submit_bio_retry:
|
||||
|
||||
/*
|
||||
* although background is preferred, no one is pending for submission.
|
||||
* don't issue workqueue for decompression but drop it directly instead.
|
||||
* don't issue decompression but drop it directly instead.
|
||||
*/
|
||||
if (!*force_fg && !nr_bios) {
|
||||
kvfree(q[JQ_SUBMIT]);
|
||||
@@ -1357,14 +1496,14 @@ submit_bio_retry:
|
||||
}
|
||||
|
||||
static void z_erofs_runqueue(struct super_block *sb,
|
||||
struct z_erofs_decompress_frontend *f,
|
||||
struct z_erofs_collector *clt,
|
||||
struct list_head *pagepool, bool force_fg)
|
||||
{
|
||||
struct z_erofs_decompressqueue io[NR_JOBQUEUES];
|
||||
|
||||
if (f->clt.owned_head == Z_EROFS_PCLUSTER_TAIL)
|
||||
if (clt->owned_head == Z_EROFS_PCLUSTER_TAIL)
|
||||
return;
|
||||
z_erofs_submit_queue(sb, f, pagepool, io, &force_fg);
|
||||
z_erofs_submit_queue(sb, clt->owned_head, pagepool, io, &force_fg);
|
||||
|
||||
/* handle bypass queue (no i/o pclusters) immediately */
|
||||
z_erofs_decompress_queue(&io[JQ_BYPASS], pagepool);
|
||||
@@ -1373,8 +1512,7 @@ static void z_erofs_runqueue(struct super_block *sb,
|
||||
return;
|
||||
|
||||
/* wait until all bios are completed */
|
||||
io_wait_event(io[JQ_SUBMIT].u.wait,
|
||||
!atomic_read(&io[JQ_SUBMIT].pending_bios));
|
||||
wait_for_completion_io(&io[JQ_SUBMIT].u.done);
|
||||
|
||||
/* handle synchronous decompress queue in the caller context */
|
||||
z_erofs_decompress_queue(&io[JQ_SUBMIT], pagepool);
|
||||
@@ -1395,7 +1533,7 @@ static int z_erofs_readpage(struct file *file, struct page *page)
|
||||
(void)z_erofs_collector_end(&f.clt);
|
||||
|
||||
/* if some compressed cluster ready, need submit them anyway */
|
||||
z_erofs_runqueue(inode->i_sb, &f, &pagepool, true);
|
||||
z_erofs_runqueue(inode->i_sb, &f.clt, &pagepool, true);
|
||||
|
||||
if (err)
|
||||
erofs_err(inode->i_sb, "failed to read, err [%d]", err);
|
||||
@@ -1465,7 +1603,7 @@ static int z_erofs_readpages(struct file *filp, struct address_space *mapping,
|
||||
|
||||
(void)z_erofs_collector_end(&f.clt);
|
||||
|
||||
z_erofs_runqueue(inode->i_sb, &f, &pagepool, sync);
|
||||
z_erofs_runqueue(inode->i_sb, &f.clt, &pagepool, sync);
|
||||
|
||||
if (f.map.mpage)
|
||||
put_page(f.map.mpage);
|
||||
|
||||
+3
-1
@@ -6,6 +6,7 @@
|
||||
#ifndef __EROFS_FS_ZDATA_H
|
||||
#define __EROFS_FS_ZDATA_H
|
||||
|
||||
#include <linux/kthread.h>
|
||||
#include "internal.h"
|
||||
#include "zpvec.h"
|
||||
|
||||
@@ -89,8 +90,9 @@ struct z_erofs_decompressqueue {
|
||||
z_erofs_next_pcluster_t head;
|
||||
|
||||
union {
|
||||
wait_queue_head_t wait;
|
||||
struct completion done;
|
||||
struct work_struct work;
|
||||
struct kthread_work kthread_work;
|
||||
} u;
|
||||
};
|
||||
|
||||
|
||||
Vendored
+6
-3
@@ -2,9 +2,9 @@ name: linux-exfat-oot CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master" ]
|
||||
branches: [ "for-kernel-version-from-4.1.0" ]
|
||||
pull_request:
|
||||
branches: [ "master" ]
|
||||
branches: [ "for-kernel-version-from-4.1.0" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
@@ -20,6 +20,7 @@ jobs:
|
||||
wget https://mirrors.edge.kernel.org/pub/linux/kernel/v4.x/linux-4.1.36.tar.gz
|
||||
tar xf linux-4.1.36.tar.gz
|
||||
mv linux-4.1.36 linux-stable
|
||||
rm -rf linux-4.1.36.tar.gz
|
||||
- name: Prerequisite for xfstests testing
|
||||
run: |
|
||||
sudo apt-get install linux-headers-$(uname -r)
|
||||
@@ -47,7 +48,9 @@ jobs:
|
||||
make -j$((`nproc`+1)) fs/exfat/exfat.ko
|
||||
- name: Run xfstests testsuite
|
||||
run: |
|
||||
cd ../linux-exfat-oot
|
||||
cd ..
|
||||
rm -rf linux-stable
|
||||
cd linux-exfat-oot
|
||||
make > /dev/null
|
||||
sudo make install > /dev/null
|
||||
sudo insmod exfat.ko
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
config EXFAT_FS
|
||||
tristate "exFAT filesystem support"
|
||||
select BUFFER_HEAD
|
||||
select NLS
|
||||
select LEGACY_DIRECT_IO
|
||||
help
|
||||
|
||||
+38
-51
@@ -6,6 +6,7 @@
|
||||
#include <linux/version.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/buffer_head.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/sched/signal.h>
|
||||
@@ -17,37 +18,21 @@
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
|
||||
static const unsigned char free_bit[] = {
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/* 0 ~ 19*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3,/* 20 ~ 39*/
|
||||
0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/* 40 ~ 59*/
|
||||
0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/* 60 ~ 79*/
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5, 0, 1, 0, 2,/* 80 ~ 99*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3,/*100 ~ 119*/
|
||||
0, 1, 0, 2, 0, 1, 0, 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*120 ~ 139*/
|
||||
0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 5,/*140 ~ 159*/
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2,/*160 ~ 179*/
|
||||
0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 6, 0, 1, 0, 2, 0, 1, 0, 3,/*180 ~ 199*/
|
||||
0, 1, 0, 2, 0, 1, 0, 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2,/*200 ~ 219*/
|
||||
0, 1, 0, 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4,/*220 ~ 239*/
|
||||
0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 /*240 ~ 254*/
|
||||
};
|
||||
#if BITS_PER_LONG == 32
|
||||
#define __le_long __le32
|
||||
#define lel_to_cpu(A) le32_to_cpu(A)
|
||||
#define cpu_to_lel(A) cpu_to_le32(A)
|
||||
#elif BITS_PER_LONG == 64
|
||||
#define __le_long __le64
|
||||
#define lel_to_cpu(A) le64_to_cpu(A)
|
||||
#define cpu_to_lel(A) cpu_to_le64(A)
|
||||
#else
|
||||
#error "BITS_PER_LONG not 32 or 64"
|
||||
#endif
|
||||
|
||||
static const unsigned char used_bit[] = {
|
||||
0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3,/* 0 ~ 19*/
|
||||
2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, 1, 2, 2, 3, 2, 3, 3, 4,/* 20 ~ 39*/
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5,/* 40 ~ 59*/
|
||||
4, 5, 5, 6, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,/* 60 ~ 79*/
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 2, 3, 3, 4,/* 80 ~ 99*/
|
||||
3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6,/*100 ~ 119*/
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4,/*120 ~ 139*/
|
||||
3, 4, 4, 5, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,/*140 ~ 159*/
|
||||
2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5,/*160 ~ 179*/
|
||||
4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5,/*180 ~ 199*/
|
||||
3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6,/*200 ~ 219*/
|
||||
5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,/*220 ~ 239*/
|
||||
4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 /*240 ~ 255*/
|
||||
};
|
||||
#ifndef ALIGN_DOWN
|
||||
#define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Allocation Bitmap Management Functions
|
||||
@@ -211,32 +196,35 @@ unsigned int exfat_find_free_bitmap(struct super_block *sb, unsigned int clu)
|
||||
{
|
||||
unsigned int i, map_i, map_b, ent_idx;
|
||||
unsigned int clu_base, clu_free;
|
||||
unsigned char k, clu_mask;
|
||||
unsigned long clu_bits, clu_mask;
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
__le_long bitval;
|
||||
|
||||
WARN_ON(clu < EXFAT_FIRST_CLUSTER);
|
||||
ent_idx = CLUSTER_TO_BITMAP_ENT(clu);
|
||||
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx & ~(BITS_PER_BYTE_MASK));
|
||||
ent_idx = ALIGN_DOWN(CLUSTER_TO_BITMAP_ENT(clu), BITS_PER_LONG);
|
||||
clu_base = BITMAP_ENT_TO_CLUSTER(ent_idx);
|
||||
clu_mask = IGNORED_BITS_REMAINED(clu, clu_base);
|
||||
|
||||
map_i = BITMAP_OFFSET_SECTOR_INDEX(sb, ent_idx);
|
||||
map_b = BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent_idx);
|
||||
|
||||
for (i = EXFAT_FIRST_CLUSTER; i < sbi->num_clusters;
|
||||
i += BITS_PER_BYTE) {
|
||||
k = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
i += BITS_PER_LONG) {
|
||||
bitval = *(__le_long *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
if (clu_mask > 0) {
|
||||
k |= clu_mask;
|
||||
bitval |= cpu_to_lel(clu_mask);
|
||||
clu_mask = 0;
|
||||
}
|
||||
if (k < 0xFF) {
|
||||
clu_free = clu_base + free_bit[k];
|
||||
if (lel_to_cpu(bitval) != ULONG_MAX) {
|
||||
clu_bits = lel_to_cpu(bitval);
|
||||
clu_free = clu_base + ffz(clu_bits);
|
||||
if (clu_free < sbi->num_clusters)
|
||||
return clu_free;
|
||||
}
|
||||
clu_base += BITS_PER_BYTE;
|
||||
clu_base += BITS_PER_LONG;
|
||||
map_b += sizeof(long);
|
||||
|
||||
if (++map_b >= sb->s_blocksize ||
|
||||
if (map_b >= sb->s_blocksize ||
|
||||
clu_base >= sbi->num_clusters) {
|
||||
if (++map_i >= sbi->map_sectors) {
|
||||
clu_base = EXFAT_FIRST_CLUSTER;
|
||||
@@ -255,25 +243,24 @@ int exfat_count_used_clusters(struct super_block *sb, unsigned int *ret_count)
|
||||
unsigned int count = 0;
|
||||
unsigned int i, map_i = 0, map_b = 0;
|
||||
unsigned int total_clus = EXFAT_DATA_CLUSTER_COUNT(sbi);
|
||||
unsigned int last_mask = total_clus & BITS_PER_BYTE_MASK;
|
||||
unsigned char clu_bits;
|
||||
const unsigned char last_bit_mask[] = {0, 0b00000001, 0b00000011,
|
||||
0b00000111, 0b00001111, 0b00011111, 0b00111111, 0b01111111};
|
||||
unsigned int last_mask = total_clus & (BITS_PER_LONG - 1);
|
||||
unsigned long *bitmap, clu_bits;
|
||||
|
||||
total_clus &= ~last_mask;
|
||||
for (i = 0; i < total_clus; i += BITS_PER_BYTE) {
|
||||
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
count += used_bit[clu_bits];
|
||||
if (++map_b >= (unsigned int)sb->s_blocksize) {
|
||||
for (i = 0; i < total_clus; i += BITS_PER_LONG) {
|
||||
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
count += hweight_long(*bitmap);
|
||||
map_b += sizeof(long);
|
||||
if (map_b >= (unsigned int)sb->s_blocksize) {
|
||||
map_i++;
|
||||
map_b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (last_mask) {
|
||||
clu_bits = *(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
clu_bits &= last_bit_mask[last_mask];
|
||||
count += used_bit[clu_bits];
|
||||
bitmap = (void *)(sbi->vol_amap[map_i]->b_data + map_b);
|
||||
clu_bits = lel_to_cpu(*(__le_long *)bitmap);
|
||||
count += hweight_long(clu_bits & BITMAP_LAST_WORD_MASK(last_mask));
|
||||
}
|
||||
|
||||
*ret_count = count;
|
||||
|
||||
+17
-10
@@ -288,7 +288,7 @@ get_new:
|
||||
|
||||
mutex_unlock(&EXFAT_SB(sb)->s_lock);
|
||||
if (!dir_emit(ctx, nb->lfn, strlen(nb->lfn), inum,
|
||||
(de.attr & ATTR_SUBDIR) ? DT_DIR : DT_REG))
|
||||
(de.attr & EXFAT_ATTR_SUBDIR) ? DT_DIR : DT_REG))
|
||||
goto out;
|
||||
ctx->pos = cpos;
|
||||
goto get_new;
|
||||
@@ -307,10 +307,17 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
WRAP_DIR_ITER(exfat_iterate) // FIXME!
|
||||
#endif
|
||||
const struct file_operations exfat_dir_operations = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read = generic_read_dir,
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 5, 0)
|
||||
.iterate_shared = shared_exfat_iterate,
|
||||
#else
|
||||
.iterate = exfat_iterate,
|
||||
#endif
|
||||
.unlocked_ioctl = exfat_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = exfat_compat_ioctl,
|
||||
@@ -359,7 +366,7 @@ unsigned int exfat_get_entry_type(struct exfat_dentry *ep)
|
||||
if (ep->type == EXFAT_VOLUME)
|
||||
return TYPE_VOLUME;
|
||||
if (ep->type == EXFAT_FILE) {
|
||||
if (le16_to_cpu(ep->dentry.file.attr) & ATTR_SUBDIR)
|
||||
if (le16_to_cpu(ep->dentry.file.attr) & EXFAT_ATTR_SUBDIR)
|
||||
return TYPE_DIR;
|
||||
return TYPE_FILE;
|
||||
}
|
||||
@@ -410,19 +417,21 @@ static void exfat_set_entry_type(struct exfat_dentry *ep, unsigned int type)
|
||||
ep->type = EXFAT_VOLUME;
|
||||
} else if (type == TYPE_DIR) {
|
||||
ep->type = EXFAT_FILE;
|
||||
ep->dentry.file.attr = cpu_to_le16(ATTR_SUBDIR);
|
||||
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_SUBDIR);
|
||||
} else if (type == TYPE_FILE) {
|
||||
ep->type = EXFAT_FILE;
|
||||
ep->dentry.file.attr = cpu_to_le16(ATTR_ARCHIVE);
|
||||
ep->dentry.file.attr = cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
}
|
||||
}
|
||||
|
||||
static void exfat_init_stream_entry(struct exfat_dentry *ep,
|
||||
unsigned char flags, unsigned int start_clu,
|
||||
unsigned long long size)
|
||||
unsigned int start_clu, unsigned long long size)
|
||||
{
|
||||
exfat_set_entry_type(ep, TYPE_STREAM);
|
||||
ep->dentry.stream.flags = flags;
|
||||
if (size == 0)
|
||||
ep->dentry.stream.flags = ALLOC_FAT_CHAIN;
|
||||
else
|
||||
ep->dentry.stream.flags = ALLOC_NO_FAT_CHAIN;
|
||||
ep->dentry.stream.start_clu = cpu_to_le32(start_clu);
|
||||
ep->dentry.stream.valid_size = cpu_to_le64(size);
|
||||
ep->dentry.stream.size = cpu_to_le64(size);
|
||||
@@ -498,9 +507,7 @@ int exfat_init_dir_entry(struct inode *inode, struct exfat_chain *p_dir,
|
||||
if (!ep)
|
||||
return -EIO;
|
||||
|
||||
exfat_init_stream_entry(ep,
|
||||
(type == TYPE_FILE) ? ALLOC_FAT_CHAIN : ALLOC_NO_FAT_CHAIN,
|
||||
start_clu, size);
|
||||
exfat_init_stream_entry(ep, start_clu, size);
|
||||
exfat_update_bh(bh, IS_DIRSYNC(inode));
|
||||
brelse(bh);
|
||||
|
||||
|
||||
+12
-8
@@ -148,8 +148,7 @@ enum {
|
||||
#define BITMAP_OFFSET_BIT_IN_SECTOR(sb, ent) (ent & BITS_PER_SECTOR_MASK(sb))
|
||||
#define BITMAP_OFFSET_BYTE_IN_SECTOR(sb, ent) \
|
||||
((ent / BITS_PER_BYTE) & ((sb)->s_blocksize - 1))
|
||||
#define BITS_PER_BYTE_MASK 0x7
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1 << ((clu) - (clu_base))) - 1)
|
||||
#define IGNORED_BITS_REMAINED(clu, clu_base) ((1UL << ((clu) - (clu_base))) - 1)
|
||||
|
||||
#define ES_ENTRY_NUM(name_len) (ES_IDX_LAST_FILENAME(name_len) + 1)
|
||||
/* 19 entries = 1 file entry + 1 stream entry + 17 filename entries */
|
||||
@@ -253,6 +252,8 @@ struct exfat_mount_options {
|
||||
discard:1, /* Issue discard requests on deletions */
|
||||
keep_last_dots:1; /* Keep trailing periods in paths */
|
||||
int time_offset; /* Offset of timestamps from UTC (in minutes) */
|
||||
/* Support creating zero-size directory, default: false */
|
||||
bool zero_size_dir;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -382,10 +383,10 @@ static inline int exfat_mode_can_hold_ro(struct inode *inode)
|
||||
static inline mode_t exfat_make_mode(struct exfat_sb_info *sbi,
|
||||
unsigned short attr, mode_t mode)
|
||||
{
|
||||
if ((attr & ATTR_READONLY) && !(attr & ATTR_SUBDIR))
|
||||
if ((attr & EXFAT_ATTR_READONLY) && !(attr & EXFAT_ATTR_SUBDIR))
|
||||
mode &= ~0222;
|
||||
|
||||
if (attr & ATTR_SUBDIR)
|
||||
if (attr & EXFAT_ATTR_SUBDIR)
|
||||
return (mode & ~sbi->options.fs_dmask) | S_IFDIR;
|
||||
|
||||
return (mode & ~sbi->options.fs_fmask) | S_IFREG;
|
||||
@@ -397,18 +398,18 @@ static inline unsigned short exfat_make_attr(struct inode *inode)
|
||||
unsigned short attr = EXFAT_I(inode)->attr;
|
||||
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
attr |= ATTR_SUBDIR;
|
||||
attr |= EXFAT_ATTR_SUBDIR;
|
||||
if (exfat_mode_can_hold_ro(inode) && !(inode->i_mode & 0222))
|
||||
attr |= ATTR_READONLY;
|
||||
attr |= EXFAT_ATTR_READONLY;
|
||||
return attr;
|
||||
}
|
||||
|
||||
static inline void exfat_save_attr(struct inode *inode, unsigned short attr)
|
||||
{
|
||||
if (exfat_mode_can_hold_ro(inode))
|
||||
EXFAT_I(inode)->attr = attr & (ATTR_RWMASK | ATTR_READONLY);
|
||||
EXFAT_I(inode)->attr = attr & (EXFAT_ATTR_RWMASK | EXFAT_ATTR_READONLY);
|
||||
else
|
||||
EXFAT_I(inode)->attr = attr & ATTR_RWMASK;
|
||||
EXFAT_I(inode)->attr = attr & EXFAT_ATTR_RWMASK;
|
||||
}
|
||||
|
||||
static inline bool exfat_is_last_sector_in_cluster(struct exfat_sb_info *sbi,
|
||||
@@ -596,6 +597,9 @@ void __exfat_fs_error(struct super_block *sb, int report, const char *fmt, ...)
|
||||
void exfat_get_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
u8 tz, __le16 time, __le16 date, u8 time_cs);
|
||||
void exfat_truncate_atime(struct timespec64 *ts);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
void exfat_truncate_inode_atime(struct inode *inode);
|
||||
#endif
|
||||
void exfat_set_entry_time(struct exfat_sb_info *sbi, struct timespec64 *ts,
|
||||
u8 *tz, __le16 *time, __le16 *date, u8 *time_cs);
|
||||
#else
|
||||
|
||||
@@ -64,15 +64,16 @@
|
||||
#define CS_DEFAULT 2
|
||||
|
||||
/* file attributes */
|
||||
#define ATTR_READONLY 0x0001
|
||||
#define ATTR_HIDDEN 0x0002
|
||||
#define ATTR_SYSTEM 0x0004
|
||||
#define ATTR_VOLUME 0x0008
|
||||
#define ATTR_SUBDIR 0x0010
|
||||
#define ATTR_ARCHIVE 0x0020
|
||||
#define EXFAT_ATTR_READONLY 0x0001
|
||||
#define EXFAT_ATTR_HIDDEN 0x0002
|
||||
#define EXFAT_ATTR_SYSTEM 0x0004
|
||||
#define EXFAT_ATTR_VOLUME 0x0008
|
||||
#define EXFAT_ATTR_SUBDIR 0x0010
|
||||
#define EXFAT_ATTR_ARCHIVE 0x0020
|
||||
|
||||
#define ATTR_RWMASK (ATTR_HIDDEN | ATTR_SYSTEM | ATTR_VOLUME | \
|
||||
ATTR_SUBDIR | ATTR_ARCHIVE)
|
||||
#define EXFAT_ATTR_RWMASK (EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM | \
|
||||
EXFAT_ATTR_VOLUME | EXFAT_ATTR_SUBDIR | \
|
||||
EXFAT_ATTR_ARCHIVE)
|
||||
|
||||
#define BOOTSEC_JUMP_BOOT_LEN 3
|
||||
#define BOOTSEC_FS_NAME_LEN 8
|
||||
|
||||
+160
-1
@@ -9,8 +9,12 @@
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/cred.h>
|
||||
#endif
|
||||
#include <linux/mount.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/fsnotify.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/msdos_fs.h>
|
||||
|
||||
#include "exfat_raw.h"
|
||||
#include "exfat_fs.h"
|
||||
@@ -26,7 +30,15 @@ static int exfat_cont_expand(struct inode *inode, loff_t size)
|
||||
return err;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_ctime = inode->i_mtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_ctime = inode->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@@ -151,7 +163,7 @@ int __exfat_truncate(struct inode *inode)
|
||||
}
|
||||
|
||||
if (ei->type == TYPE_FILE)
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
|
||||
/*
|
||||
* update the directory entry
|
||||
@@ -265,7 +277,11 @@ int exfat_getattr(struct vfsmount *mnt, struct dentry *dentry,
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
generic_fillattr(&nop_mnt_idmap, request_mask, inode, stat);
|
||||
#else
|
||||
generic_fillattr(&nop_mnt_idmap, inode, stat);
|
||||
#endif
|
||||
#else
|
||||
generic_fillattr(&init_user_ns, inode, stat);
|
||||
#endif
|
||||
@@ -355,11 +371,23 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
setattr_copy(&nop_mnt_idmap, inode, attr);
|
||||
@@ -370,6 +398,7 @@ int exfat_setattr(struct dentry *dentry, struct iattr *attr)
|
||||
setattr_copy(inode, attr);
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
|
||||
if (attr->ia_valid & ATTR_SIZE) {
|
||||
error = exfat_block_truncate_page(inode, attr->ia_size);
|
||||
@@ -390,6 +419,131 @@ out:
|
||||
return error;
|
||||
}
|
||||
|
||||
/*
|
||||
* modified ioctls from fat/file.c by Welmer Almesberger
|
||||
*/
|
||||
static int exfat_ioctl_get_attributes(struct inode *inode, u32 __user *user_attr)
|
||||
{
|
||||
u32 attr;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||
inode_lock_shared(inode);
|
||||
#else
|
||||
mutex_lock(&inode->i_mutex);
|
||||
#endif
|
||||
attr = exfat_make_attr(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||
inode_unlock_shared(inode);
|
||||
#else
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
#endif
|
||||
|
||||
return put_user(attr, user_attr);
|
||||
}
|
||||
|
||||
static int exfat_ioctl_set_attributes(struct file *file, u32 __user *user_attr)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(inode->i_sb);
|
||||
int is_dir = S_ISDIR(inode->i_mode);
|
||||
u32 attr, oldattr;
|
||||
struct iattr ia;
|
||||
int err;
|
||||
|
||||
err = get_user(attr, user_attr);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
err = mnt_want_write_file(file);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
inode_lock(inode);
|
||||
#else
|
||||
mutex_lock(&inode->i_mutex);
|
||||
#endif
|
||||
|
||||
oldattr = exfat_make_attr(inode);
|
||||
|
||||
/*
|
||||
* Mask attributes so we don't set reserved fields.
|
||||
*/
|
||||
attr &= (EXFAT_ATTR_READONLY | EXFAT_ATTR_HIDDEN | EXFAT_ATTR_SYSTEM |
|
||||
EXFAT_ATTR_ARCHIVE);
|
||||
attr |= (is_dir ? EXFAT_ATTR_SUBDIR : 0);
|
||||
|
||||
/* Equivalent to a chmod() */
|
||||
ia.ia_valid = ATTR_MODE | ATTR_CTIME;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
|
||||
ia.ia_ctime = current_time(inode);
|
||||
#else
|
||||
ia.ia_ctime = current_fs_time(inode->i_sb);
|
||||
#endif
|
||||
if (is_dir)
|
||||
ia.ia_mode = exfat_make_mode(sbi, attr, 0777);
|
||||
else
|
||||
ia.ia_mode = exfat_make_mode(sbi, attr, 0666 | (inode->i_mode & 0111));
|
||||
|
||||
/* The root directory has no attributes */
|
||||
if (inode->i_ino == EXFAT_ROOT_INO && attr != EXFAT_ATTR_SUBDIR) {
|
||||
err = -EINVAL;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
if (((attr | oldattr) & EXFAT_ATTR_SYSTEM) &&
|
||||
!capable(CAP_LINUX_IMMUTABLE)) {
|
||||
err = -EPERM;
|
||||
goto out_unlock_inode;
|
||||
}
|
||||
|
||||
/*
|
||||
* The security check is questionable... We single
|
||||
* out the RO attribute for checking by the security
|
||||
* module, just because it maps to a file mode.
|
||||
*/
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
err = security_inode_setattr(file_mnt_idmap(file),
|
||||
file->f_path.dentry, &ia);
|
||||
#else
|
||||
err = security_inode_setattr(file_mnt_user_ns(file),
|
||||
file->f_path.dentry, &ia);
|
||||
#endif
|
||||
#else
|
||||
err = security_inode_setattr(file->f_path.dentry, &ia);
|
||||
#endif
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 3, 0)
|
||||
/* This MUST be done before doing anything irreversible... */
|
||||
err = exfat_setattr(file_mnt_idmap(file), file->f_path.dentry, &ia);
|
||||
#else
|
||||
err = exfat_setattr(file_mnt_user_ns(file), file->f_path.dentry, &ia);
|
||||
#endif
|
||||
#else
|
||||
err = exfat_setattr(file->f_path.dentry, &ia);
|
||||
#endif
|
||||
if (err)
|
||||
goto out_unlock_inode;
|
||||
|
||||
fsnotify_change(file->f_path.dentry, ia.ia_valid);
|
||||
|
||||
exfat_save_attr(inode, attr);
|
||||
mark_inode_dirty(inode);
|
||||
out_unlock_inode:
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
|
||||
inode_unlock(inode);
|
||||
#else
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
#endif
|
||||
mnt_drop_write_file(file);
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0)
|
||||
@@ -432,8 +586,13 @@ static int exfat_ioctl_fitrim(struct inode *inode, unsigned long arg)
|
||||
long exfat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct inode *inode = file_inode(filp);
|
||||
u32 __user *user_attr = (u32 __user *)arg;
|
||||
|
||||
switch (cmd) {
|
||||
case FAT_IOCTL_GET_ATTRIBUTES:
|
||||
return exfat_ioctl_get_attributes(inode, user_attr);
|
||||
case FAT_IOCTL_SET_ATTRIBUTES:
|
||||
return exfat_ioctl_set_attributes(filp, user_attr);
|
||||
case FITRIM:
|
||||
return exfat_ioctl_fitrim(inode, arg);
|
||||
default:
|
||||
|
||||
+49
-3
@@ -28,6 +28,9 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
struct exfat_sb_info *sbi = EXFAT_SB(sb);
|
||||
struct exfat_inode_info *ei = EXFAT_I(inode);
|
||||
bool is_dir = (ei->type == TYPE_DIR) ? true : false;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
struct timespec64 ts;
|
||||
#endif
|
||||
|
||||
if (inode->i_ino == EXFAT_ROOT_INO)
|
||||
return 0;
|
||||
@@ -57,6 +60,20 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
&ep->dentry.file.create_time,
|
||||
&ep->dentry.file.create_date,
|
||||
&ep->dentry.file.create_time_cs);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
ts = inode_get_mtime(inode);
|
||||
exfat_set_entry_time(sbi, &ts,
|
||||
&ep->dentry.file.modify_tz,
|
||||
&ep->dentry.file.modify_time,
|
||||
&ep->dentry.file.modify_date,
|
||||
&ep->dentry.file.modify_time_cs);
|
||||
ts = inode_get_atime(inode);
|
||||
exfat_set_entry_time(sbi, &ts,
|
||||
&ep->dentry.file.access_tz,
|
||||
&ep->dentry.file.access_time,
|
||||
&ep->dentry.file.access_date,
|
||||
NULL);
|
||||
#else
|
||||
exfat_set_entry_time(sbi, &inode->i_mtime,
|
||||
&ep->dentry.file.modify_tz,
|
||||
&ep->dentry.file.modify_time,
|
||||
@@ -67,6 +84,7 @@ int __exfat_write_inode(struct inode *inode, int sync)
|
||||
&ep->dentry.file.access_time,
|
||||
&ep->dentry.file.access_date,
|
||||
NULL);
|
||||
#endif
|
||||
|
||||
/* File size should be zero if there is no cluster allocated */
|
||||
on_disk_size = i_size_read(inode);
|
||||
@@ -380,7 +398,15 @@ static void exfat_write_failed(struct address_space *mapping, loff_t to)
|
||||
if (to > i_size_read(inode)) {
|
||||
truncate_pagecache(inode, i_size_read(inode));
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@@ -436,13 +462,21 @@ static int exfat_write_end(struct file *file, struct address_space *mapping,
|
||||
if (err < len)
|
||||
exfat_write_failed(mapping, pos+len);
|
||||
|
||||
if (!(err < 0) && !(ei->attr & ATTR_ARCHIVE)) {
|
||||
if (!(err < 0) && !(ei->attr & EXFAT_ATTR_ARCHIVE)) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, inode_set_ctime_current(inode));
|
||||
#else
|
||||
inode->i_mtime = inode_set_ctime_current(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
mark_inode_dirty(inode);
|
||||
}
|
||||
|
||||
@@ -629,7 +663,7 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
inode->i_generation = prandom_u32();
|
||||
#endif
|
||||
|
||||
if (info->attr & ATTR_SUBDIR) { /* directory */
|
||||
if (info->attr & EXFAT_ATTR_SUBDIR) { /* directory */
|
||||
inode->i_generation &= ~1;
|
||||
inode->i_mode = exfat_make_mode(sbi, info->attr, 0777);
|
||||
inode->i_op = &exfat_dir_inode_operations;
|
||||
@@ -658,10 +692,22 @@ static int exfat_fill_inode(struct inode *inode, struct exfat_dir_entry *info)
|
||||
exfat_save_attr(inode, info->attr);
|
||||
|
||||
inode->i_blocks = round_up(i_size_read(inode), sbi->cluster_size) >> 9;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(inode, info->mtime);
|
||||
#else
|
||||
inode->i_mtime = info->mtime;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode_set_ctime_to_ts(inode, info->mtime);
|
||||
#else
|
||||
inode->i_ctime = info->mtime;
|
||||
#endif
|
||||
ei->i_crtime = info->crtime;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_atime_to_ts(inode, info->atime);
|
||||
#else
|
||||
inode->i_atime = info->atime;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -163,6 +163,16 @@ void exfat_truncate_atime(struct timespec *ts)
|
||||
ts->tv_nsec = 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
void exfat_truncate_inode_atime(struct inode *inode)
|
||||
{
|
||||
struct timespec64 atime = inode_get_atime(inode);
|
||||
|
||||
exfat_truncate_atime(&atime);
|
||||
inode_set_atime_to_ts(inode, atime);
|
||||
}
|
||||
#endif
|
||||
|
||||
u16 exfat_calc_chksum16(void *data, int len, u16 chksum, int type)
|
||||
{
|
||||
int i;
|
||||
|
||||
+130
-26
@@ -378,14 +378,20 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (exfat_check_max_dentries(inode))
|
||||
return -ENOSPC;
|
||||
|
||||
/*
|
||||
* Allocate new cluster to this directory
|
||||
*/
|
||||
if (ei->start_clu != EXFAT_EOF_CLUSTER) {
|
||||
/* we trust p_dir->size regardless of FAT type */
|
||||
if (exfat_find_last_cluster(sb, p_dir, &last_clu))
|
||||
return -EIO;
|
||||
|
||||
/*
|
||||
* Allocate new cluster to this directory
|
||||
*/
|
||||
exfat_chain_set(&clu, last_clu + 1, 0, p_dir->flags);
|
||||
} else {
|
||||
/* This directory is empty */
|
||||
exfat_chain_set(&clu, EXFAT_EOF_CLUSTER, 0,
|
||||
ALLOC_NO_FAT_CHAIN);
|
||||
}
|
||||
|
||||
/* allocate a cluster */
|
||||
ret = exfat_alloc_cluster(inode, 1, &clu, IS_DIRSYNC(inode));
|
||||
@@ -395,6 +401,11 @@ static int exfat_find_empty_entry(struct inode *inode,
|
||||
if (exfat_zeroed_cluster(inode, clu.dir))
|
||||
return -EIO;
|
||||
|
||||
if (ei->start_clu == EXFAT_EOF_CLUSTER) {
|
||||
ei->start_clu = clu.dir;
|
||||
p_dir->dir = clu.dir;
|
||||
}
|
||||
|
||||
/* append to the FAT chain */
|
||||
if (clu.flags != p_dir->flags) {
|
||||
/* no-fat-chain bit is disabled,
|
||||
@@ -534,7 +545,7 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (type == TYPE_DIR) {
|
||||
if (type == TYPE_DIR && !sbi->options.zero_size_dir) {
|
||||
ret = exfat_alloc_new_dir(inode, &clu);
|
||||
if (ret)
|
||||
goto out;
|
||||
@@ -561,12 +572,15 @@ static int exfat_add_entry(struct inode *inode, const char *path,
|
||||
info->type = type;
|
||||
|
||||
if (type == TYPE_FILE) {
|
||||
info->attr = ATTR_ARCHIVE;
|
||||
info->attr = EXFAT_ATTR_ARCHIVE;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
info->size = 0;
|
||||
info->num_subdirs = 0;
|
||||
} else {
|
||||
info->attr = ATTR_SUBDIR;
|
||||
info->attr = EXFAT_ATTR_SUBDIR;
|
||||
if (sbi->options.zero_size_dir)
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
else
|
||||
info->start_clu = start_clu;
|
||||
info->size = clu_size;
|
||||
info->num_subdirs = EXFAT_MIN_SUBDIR;
|
||||
@@ -611,7 +625,15 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
#else
|
||||
dir->i_mtime = inode_set_ctime_current(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@@ -632,14 +654,23 @@ static int exfat_create(struct inode *dir, struct dentry *dentry, umode_t mode,
|
||||
inode->i_version++;
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
@@ -709,7 +740,7 @@ static int exfat_find(struct inode *dir, struct qstr *qname,
|
||||
info->type = exfat_get_entry_type(ep);
|
||||
info->attr = le16_to_cpu(ep->dentry.file.attr);
|
||||
info->size = le64_to_cpu(ep2->dentry.stream.valid_size);
|
||||
if ((info->type == TYPE_FILE) && (info->size == 0)) {
|
||||
if (info->size == 0) {
|
||||
info->flags = ALLOC_NO_FAT_CHAIN;
|
||||
info->start_clu = EXFAT_EOF_CLUSTER;
|
||||
} else {
|
||||
@@ -888,24 +919,43 @@ static int exfat_unlink(struct inode *dir, struct dentry *dentry)
|
||||
#else
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(dir);
|
||||
exfat_truncate_inode_atime(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
mark_inode_dirty(dir);
|
||||
|
||||
clear_nlink(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = dir->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
exfat_unhash_inode(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
@@ -950,7 +1000,15 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#endif
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
inode_set_mtime_to_ts(dir, inode_set_ctime_current(dir));
|
||||
#else
|
||||
dir->i_mtime = inode_set_ctime_current(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_ctime = dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@@ -971,14 +1029,23 @@ static int exfat_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)
|
||||
#else
|
||||
inode->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
EXFAT_I(inode)->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = EXFAT_I(inode)->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime =
|
||||
EXFAT_I(inode)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
/* timestamp is already written, so mark_inode_dirty() is unneeded. */
|
||||
|
||||
d_instantiate(dentry, inode);
|
||||
@@ -1000,6 +1067,9 @@ static int exfat_check_dir_empty(struct super_block *sb,
|
||||
|
||||
dentries_per_clu = sbi->dentries_per_clu;
|
||||
|
||||
if (p_dir->dir == EXFAT_EOF_CLUSTER)
|
||||
return 0;
|
||||
|
||||
exfat_chain_dup(&clu, p_dir);
|
||||
|
||||
while (clu.dir != EXFAT_EOF_CLUSTER) {
|
||||
@@ -1093,12 +1163,21 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
#else
|
||||
dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
dir->i_mtime = dir->i_atime = current_time(dir);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(dir);
|
||||
exfat_truncate_inode_atime(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
dir->i_mtime = dir->i_atime = inode_set_ctime_current(dir);
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = dir->i_ctime = current_time(dir);
|
||||
#endif
|
||||
#else
|
||||
dir->i_mtime = dir->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(dir))
|
||||
exfat_sync_inode(dir);
|
||||
else
|
||||
@@ -1106,12 +1185,21 @@ static int exfat_rmdir(struct inode *dir, struct dentry *dentry)
|
||||
drop_nlink(dir);
|
||||
|
||||
clear_nlink(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
inode->i_mtime = inode->i_atime = current_time(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = CURRENT_TIME_SEC;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
exfat_unhash_inode(inode);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||
exfat_d_version_set(dentry, inode_query_iversion(dir));
|
||||
@@ -1160,8 +1248,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
|
||||
*epnew = *epold;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(new_bh, sync);
|
||||
brelse(old_bh);
|
||||
@@ -1192,8 +1280,8 @@ static int exfat_rename_file(struct inode *inode, struct exfat_chain *p_dir,
|
||||
ei->entry = newentry;
|
||||
} else {
|
||||
if (exfat_get_entry_type(epold) == TYPE_FILE) {
|
||||
epold->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epold->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(old_bh, sync);
|
||||
brelse(old_bh);
|
||||
@@ -1241,8 +1329,8 @@ static int exfat_move_file(struct inode *inode, struct exfat_chain *p_olddir,
|
||||
|
||||
*epnew = *epmov;
|
||||
if (exfat_get_entry_type(epnew) == TYPE_FILE) {
|
||||
epnew->dentry.file.attr |= cpu_to_le16(ATTR_ARCHIVE);
|
||||
ei->attr |= ATTR_ARCHIVE;
|
||||
epnew->dentry.file.attr |= cpu_to_le16(EXFAT_ATTR_ARCHIVE);
|
||||
ei->attr |= EXFAT_ATTR_ARCHIVE;
|
||||
}
|
||||
exfat_update_bh(new_bh, IS_DIRSYNC(inode));
|
||||
brelse(mov_bh);
|
||||
@@ -1383,7 +1471,8 @@ static int __exfat_rename(struct inode *old_parent_inode,
|
||||
}
|
||||
|
||||
/* Free the clusters if new_inode is a dir(as if exfat_rmdir) */
|
||||
if (new_entry_type == TYPE_DIR) {
|
||||
if (new_entry_type == TYPE_DIR &&
|
||||
new_ei->start_clu != EXFAT_EOF_CLUSTER) {
|
||||
/* new_ei, new_clu_to_free */
|
||||
struct exfat_chain new_clu_to_free;
|
||||
|
||||
@@ -1463,13 +1552,22 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
new_dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
simple_rename_timestamp(old_dir, old_dentry, new_dir, new_dentry);
|
||||
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
|
||||
#else
|
||||
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
|
||||
EXFAT_I(new_dir)->i_crtime = current_time(new_dir);
|
||||
#endif
|
||||
#else
|
||||
new_dir->i_ctime = new_dir->i_mtime = new_dir->i_atime =
|
||||
EXFAT_I(new_dir)->i_crtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
exfat_truncate_inode_atime(new_dir);
|
||||
#else
|
||||
exfat_truncate_atime(&new_dir->i_atime);
|
||||
#endif
|
||||
if (IS_DIRSYNC(new_dir))
|
||||
exfat_sync_inode(new_dir);
|
||||
else
|
||||
@@ -1496,7 +1594,9 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
old_dir->i_version++;
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(6, 6, 0)
|
||||
old_dir->i_ctime = old_dir->i_mtime = current_time(old_dir);
|
||||
#endif
|
||||
#else
|
||||
old_dir->i_ctime = old_dir->i_mtime = CURRENT_TIME_SEC;
|
||||
#endif
|
||||
@@ -1518,8 +1618,12 @@ static int exfat_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||
WARN_ON(new_inode->i_nlink == 0);
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
EXFAT_I(new_inode)->i_crtime = current_time(new_inode);
|
||||
#else
|
||||
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
|
||||
current_time(new_inode);
|
||||
#endif
|
||||
#else
|
||||
new_inode->i_ctime = EXFAT_I(new_inode)->i_crtime =
|
||||
CURRENT_TIME_SEC;
|
||||
|
||||
+23
-2
@@ -195,6 +195,8 @@ static int exfat_show_options(struct seq_file *m, struct dentry *root)
|
||||
seq_puts(m, ",sys_tz");
|
||||
else if (opts->time_offset)
|
||||
seq_printf(m, ",time_offset=%d", opts->time_offset);
|
||||
if (opts->zero_size_dir)
|
||||
seq_puts(m, ",zero_size_dir");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -278,6 +280,7 @@ enum {
|
||||
Opt_keep_last_dots,
|
||||
Opt_sys_tz,
|
||||
Opt_time_offset,
|
||||
Opt_zero_size_dir,
|
||||
|
||||
/* Deprecated options */
|
||||
Opt_utf8,
|
||||
@@ -323,6 +326,7 @@ static const struct fs_parameter_spec exfat_param_specs[] = {
|
||||
fsparam_flag("keep_last_dots", Opt_keep_last_dots),
|
||||
fsparam_flag("sys_tz", Opt_sys_tz),
|
||||
fsparam_s32("time_offset", Opt_time_offset),
|
||||
fsparam_flag("zero_size_dir", Opt_zero_size_dir),
|
||||
__fsparam(NULL, "utf8", Opt_utf8, fs_param_deprecated,
|
||||
NULL),
|
||||
__fsparam(NULL, "debug", Opt_debug, fs_param_deprecated,
|
||||
@@ -403,6 +407,9 @@ static int exfat_parse_param(struct fs_context *fc, struct fs_parameter *param)
|
||||
return -EINVAL;
|
||||
opts->time_offset = result.int_32;
|
||||
break;
|
||||
case Opt_zero_size_dir:
|
||||
opts->zero_size_dir = true;
|
||||
break;
|
||||
case Opt_utf8:
|
||||
case Opt_debug:
|
||||
case Opt_namecase:
|
||||
@@ -435,6 +442,7 @@ enum {
|
||||
Opt_debug,
|
||||
Opt_namecase,
|
||||
Opt_codepage,
|
||||
Opt_zero_size_dir,
|
||||
Opt_fs,
|
||||
};
|
||||
|
||||
@@ -454,6 +462,7 @@ static const match_table_t exfat_tokens = {
|
||||
{Opt_namecase, "namecase=%u"},
|
||||
{Opt_debug, "debug"},
|
||||
{Opt_utf8, "utf8"},
|
||||
{Opt_zero_size_dir, "zero_size_dir"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
@@ -545,6 +554,9 @@ static int parse_options(struct super_block *sb, char *options, int silent,
|
||||
case Opt_namecase:
|
||||
case Opt_codepage:
|
||||
break;
|
||||
case Opt_zero_size_dir:
|
||||
opts->zero_size_dir = true;
|
||||
break;
|
||||
default:
|
||||
if (!silent) {
|
||||
exfat_err(sb,
|
||||
@@ -628,7 +640,7 @@ static int exfat_read_root(struct inode *inode)
|
||||
inode->i_version++;
|
||||
#endif
|
||||
inode->i_generation = 0;
|
||||
inode->i_mode = exfat_make_mode(sbi, ATTR_SUBDIR, 0777);
|
||||
inode->i_mode = exfat_make_mode(sbi, EXFAT_ATTR_SUBDIR, 0777);
|
||||
inode->i_op = &exfat_dir_inode_operations;
|
||||
inode->i_fop = &exfat_dir_operations;
|
||||
|
||||
@@ -637,15 +649,24 @@ static int exfat_read_root(struct inode *inode)
|
||||
ei->i_size_aligned = i_size_read(inode);
|
||||
ei->i_size_ondisk = i_size_read(inode);
|
||||
|
||||
exfat_save_attr(inode, ATTR_SUBDIR);
|
||||
exfat_save_attr(inode, EXFAT_ATTR_SUBDIR);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
||||
ei->i_crtime = simple_inode_init_ts(inode);
|
||||
exfat_truncate_inode_atime(inode);
|
||||
#else
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||
inode->i_mtime = inode->i_atime = ei->i_crtime = inode_set_ctime_current(inode);
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
|
||||
current_time(inode);
|
||||
#endif
|
||||
#else
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = ei->i_crtime =
|
||||
CURRENT_TIME_SEC;
|
||||
#endif
|
||||
exfat_truncate_atime(&inode->i_atime);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+10
-5
@@ -5192,11 +5192,16 @@ __acquires(bitlock)
|
||||
static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb,
|
||||
ext4_group_t grp)
|
||||
{
|
||||
if (grp < ext4_get_groups_count(sb))
|
||||
return EXT4_CLUSTERS_PER_GROUP(sb) - 1;
|
||||
return (ext4_blocks_count(EXT4_SB(sb)->s_es) -
|
||||
ext4_group_first_block_no(sb, grp) - 1) >>
|
||||
EXT4_CLUSTER_BITS(sb);
|
||||
unsigned long nr_clusters_in_group;
|
||||
|
||||
if (grp < (ext4_get_groups_count(sb) - 1))
|
||||
nr_clusters_in_group = EXT4_CLUSTERS_PER_GROUP(sb);
|
||||
else
|
||||
nr_clusters_in_group = (ext4_blocks_count(EXT4_SB(sb)->s_es) -
|
||||
ext4_group_first_block_no(sb, grp))
|
||||
>> EXT4_CLUSTER_BITS(sb);
|
||||
|
||||
return nr_clusters_in_group - 1;
|
||||
}
|
||||
|
||||
static bool ext4_trim_interrupted(void)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Copyright (c) 2016-2017, 2019-2020, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2024, Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
*/
|
||||
|
||||
#ifndef _DT_BINDINGS_CLK_MSM_GCC_660_H
|
||||
@@ -195,5 +196,6 @@
|
||||
#define GCC_USB_20_BCR 6
|
||||
#define GCC_USB_30_BCR 7
|
||||
#define GCC_USB_PHY_CFG_AHB2PHY_BCR 8
|
||||
#define GCC_SDCC1_BCR 9
|
||||
|
||||
#endif
|
||||
@@ -1678,6 +1678,9 @@ do { \
|
||||
WARN_ONCE(condition, "%s %s: " format, \
|
||||
dev_driver_string(dev), dev_name(dev), ## arg)
|
||||
|
||||
extern __printf(3, 4)
|
||||
int dev_err_probe(const struct device *dev, int err, const char *fmt, ...);
|
||||
|
||||
/* Create alias, so I can be autoloaded. */
|
||||
#define MODULE_ALIAS_CHARDEV(major,minor) \
|
||||
MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor))
|
||||
|
||||
@@ -220,6 +220,43 @@ static inline int page_cache_add_speculative(struct page *page, int count)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* attach_page_private - Attach private data to a page.
|
||||
* @page: Page to attach data to.
|
||||
* @data: Data to attach to page.
|
||||
*
|
||||
* Attaching private data to a page increments the page's reference count.
|
||||
* The data must be detached before the page will be freed.
|
||||
*/
|
||||
static inline void attach_page_private(struct page *page, void *data)
|
||||
{
|
||||
get_page(page);
|
||||
set_page_private(page, (unsigned long)data);
|
||||
SetPagePrivate(page);
|
||||
}
|
||||
|
||||
/**
|
||||
* detach_page_private - Detach private data from a page.
|
||||
* @page: Page to detach data from.
|
||||
*
|
||||
* Removes the data that was previously attached to the page and decrements
|
||||
* the refcount on the page.
|
||||
*
|
||||
* Return: Data that was attached to the page.
|
||||
*/
|
||||
static inline void *detach_page_private(struct page *page)
|
||||
{
|
||||
void *data = (void *)page_private(page);
|
||||
|
||||
if (!PagePrivate(page))
|
||||
return NULL;
|
||||
ClearPagePrivate(page);
|
||||
set_page_private(page, 0);
|
||||
put_page(page);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_NUMA
|
||||
extern struct page *__page_cache_alloc(gfp_t gfp);
|
||||
#else
|
||||
|
||||
@@ -1863,6 +1863,9 @@ extern int idle_cpu(int cpu);
|
||||
extern int available_idle_cpu(int cpu);
|
||||
extern int sched_setscheduler(struct task_struct *, int, const struct sched_param *);
|
||||
extern int sched_setscheduler_nocheck(struct task_struct *, int, const struct sched_param *);
|
||||
extern int sched_set_fifo(struct task_struct *p);
|
||||
extern int sched_set_fifo_low(struct task_struct *p);
|
||||
extern int sched_set_normal(struct task_struct *p, int nice);
|
||||
extern int sched_setattr(struct task_struct *, const struct sched_attr *);
|
||||
extern int sched_setattr_nocheck(struct task_struct *, const struct sched_attr *);
|
||||
extern struct task_struct *idle_task(int cpu);
|
||||
|
||||
@@ -153,6 +153,7 @@ struct spi_device {
|
||||
#define SPI_MODE_1 (0|SPI_CPHA)
|
||||
#define SPI_MODE_2 (SPI_CPOL|0)
|
||||
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
|
||||
#define SPI_MODE_X_MASK (SPI_CPOL|SPI_CPHA)
|
||||
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
|
||||
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
|
||||
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _LINUX_UNITS_H
|
||||
#define _LINUX_UNITS_H
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#define HZ_PER_KHZ 1000UL
|
||||
#define KHZ_PER_MHZ 1000UL
|
||||
#define HZ_PER_MHZ 1000000UL
|
||||
|
||||
#define MILLIWATT_PER_WATT 1000UL
|
||||
#define MICROWATT_PER_MILLIWATT 1000UL
|
||||
#define MICROWATT_PER_WATT 1000000UL
|
||||
|
||||
#define ABSOLUTE_ZERO_MILLICELSIUS -273150
|
||||
|
||||
static inline long milli_kelvin_to_millicelsius(long t)
|
||||
{
|
||||
return t + ABSOLUTE_ZERO_MILLICELSIUS;
|
||||
}
|
||||
|
||||
static inline long millicelsius_to_milli_kelvin(long t)
|
||||
{
|
||||
return t - ABSOLUTE_ZERO_MILLICELSIUS;
|
||||
}
|
||||
|
||||
#define MILLIDEGREE_PER_DEGREE 1000
|
||||
#define MILLIDEGREE_PER_DECIDEGREE 100
|
||||
|
||||
static inline long kelvin_to_millicelsius(long t)
|
||||
{
|
||||
return milli_kelvin_to_millicelsius(t * MILLIDEGREE_PER_DEGREE);
|
||||
}
|
||||
|
||||
static inline long millicelsius_to_kelvin(long t)
|
||||
{
|
||||
t = millicelsius_to_milli_kelvin(t);
|
||||
|
||||
return DIV_ROUND_CLOSEST(t, MILLIDEGREE_PER_DEGREE);
|
||||
}
|
||||
|
||||
static inline long deci_kelvin_to_celsius(long t)
|
||||
{
|
||||
t = milli_kelvin_to_millicelsius(t * MILLIDEGREE_PER_DECIDEGREE);
|
||||
|
||||
return DIV_ROUND_CLOSEST(t, MILLIDEGREE_PER_DEGREE);
|
||||
}
|
||||
|
||||
static inline long celsius_to_deci_kelvin(long t)
|
||||
{
|
||||
t = millicelsius_to_milli_kelvin(t * MILLIDEGREE_PER_DEGREE);
|
||||
|
||||
return DIV_ROUND_CLOSEST(t, MILLIDEGREE_PER_DECIDEGREE);
|
||||
}
|
||||
|
||||
/**
|
||||
* deci_kelvin_to_millicelsius_with_offset - convert Kelvin to Celsius
|
||||
* @t: temperature value in decidegrees Kelvin
|
||||
* @offset: difference between Kelvin and Celsius in millidegrees
|
||||
*
|
||||
* Return: temperature value in millidegrees Celsius
|
||||
*/
|
||||
static inline long deci_kelvin_to_millicelsius_with_offset(long t, long offset)
|
||||
{
|
||||
return t * MILLIDEGREE_PER_DECIDEGREE - offset;
|
||||
}
|
||||
|
||||
static inline long deci_kelvin_to_millicelsius(long t)
|
||||
{
|
||||
return milli_kelvin_to_millicelsius(t * MILLIDEGREE_PER_DECIDEGREE);
|
||||
}
|
||||
|
||||
static inline long millicelsius_to_deci_kelvin(long t)
|
||||
{
|
||||
t = millicelsius_to_milli_kelvin(t);
|
||||
|
||||
return DIV_ROUND_CLOSEST(t, MILLIDEGREE_PER_DECIDEGREE);
|
||||
}
|
||||
|
||||
static inline long kelvin_to_celsius(long t)
|
||||
{
|
||||
return t + DIV_ROUND_CLOSEST(ABSOLUTE_ZERO_MILLICELSIUS,
|
||||
MILLIDEGREE_PER_DEGREE);
|
||||
}
|
||||
|
||||
static inline long celsius_to_kelvin(long t)
|
||||
{
|
||||
return t - DIV_ROUND_CLOSEST(ABSOLUTE_ZERO_MILLICELSIUS,
|
||||
MILLIDEGREE_PER_DEGREE);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_UNITS_H */
|
||||
@@ -262,7 +262,6 @@ static inline void llc_pdu_header_init(struct sk_buff *skb, u8 type,
|
||||
*/
|
||||
static inline void llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa)
|
||||
{
|
||||
if (skb->protocol == htons(ETH_P_802_2))
|
||||
memcpy(sa, eth_hdr(skb)->h_source, ETH_ALEN);
|
||||
}
|
||||
|
||||
@@ -275,7 +274,6 @@ static inline void llc_pdu_decode_sa(struct sk_buff *skb, u8 *sa)
|
||||
*/
|
||||
static inline void llc_pdu_decode_da(struct sk_buff *skb, u8 *da)
|
||||
{
|
||||
if (skb->protocol == htons(ETH_P_802_2))
|
||||
memcpy(da, eth_hdr(skb)->h_dest, ETH_ALEN);
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define SQUASHFS_MAGIC 0x73717368
|
||||
#define ECRYPTFS_SUPER_MAGIC 0xf15f
|
||||
#define EFS_SUPER_MAGIC 0x414A53
|
||||
#define EROFS_SUPER_MAGIC_V1 0xE0F5E1E2
|
||||
#define EXT2_SUPER_MAGIC 0xEF53
|
||||
#define EXT3_SUPER_MAGIC 0xEF53
|
||||
#define XENFS_SUPER_MAGIC 0xabba1974
|
||||
|
||||
+19
-19
@@ -596,11 +596,11 @@ static int crc32_threadfn(void *data)
|
||||
unsigned i;
|
||||
|
||||
while (1) {
|
||||
wait_event(d->go, atomic_read(&d->ready) ||
|
||||
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop()) {
|
||||
d->thr = NULL;
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
break;
|
||||
}
|
||||
@@ -609,7 +609,7 @@ static int crc32_threadfn(void *data)
|
||||
for (i = 0; i < d->run_threads; i++)
|
||||
*d->crc32 = crc32_le(*d->crc32,
|
||||
d->unc[i], *d->unc_len[i]);
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
}
|
||||
return 0;
|
||||
@@ -639,12 +639,12 @@ static int lzo_compress_threadfn(void *data)
|
||||
struct cmp_data *d = data;
|
||||
|
||||
while (1) {
|
||||
wait_event(d->go, atomic_read(&d->ready) ||
|
||||
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop()) {
|
||||
d->thr = NULL;
|
||||
d->ret = -1;
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
break;
|
||||
}
|
||||
@@ -653,7 +653,7 @@ static int lzo_compress_threadfn(void *data)
|
||||
d->ret = lzo1x_1_compress(d->unc, d->unc_len,
|
||||
d->cmp + LZO_HEADER, &d->cmp_len,
|
||||
d->wrk);
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
}
|
||||
return 0;
|
||||
@@ -791,7 +791,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
|
||||
data[thr].unc_len = off;
|
||||
|
||||
atomic_set(&data[thr].ready, 1);
|
||||
atomic_set_release(&data[thr].ready, 1);
|
||||
wake_up(&data[thr].go);
|
||||
}
|
||||
|
||||
@@ -799,12 +799,12 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
break;
|
||||
|
||||
crc->run_threads = thr;
|
||||
atomic_set(&crc->ready, 1);
|
||||
atomic_set_release(&crc->ready, 1);
|
||||
wake_up(&crc->go);
|
||||
|
||||
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
|
||||
wait_event(data[thr].done,
|
||||
atomic_read(&data[thr].stop));
|
||||
atomic_read_acquire(&data[thr].stop));
|
||||
atomic_set(&data[thr].stop, 0);
|
||||
|
||||
ret = data[thr].ret;
|
||||
@@ -843,7 +843,7 @@ static int save_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
}
|
||||
|
||||
wait_event(crc->done, atomic_read(&crc->stop));
|
||||
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
||||
atomic_set(&crc->stop, 0);
|
||||
}
|
||||
|
||||
@@ -1124,12 +1124,12 @@ static int lzo_decompress_threadfn(void *data)
|
||||
struct dec_data *d = data;
|
||||
|
||||
while (1) {
|
||||
wait_event(d->go, atomic_read(&d->ready) ||
|
||||
wait_event(d->go, atomic_read_acquire(&d->ready) ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop()) {
|
||||
d->thr = NULL;
|
||||
d->ret = -1;
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
break;
|
||||
}
|
||||
@@ -1142,7 +1142,7 @@ static int lzo_decompress_threadfn(void *data)
|
||||
flush_icache_range((unsigned long)d->unc,
|
||||
(unsigned long)d->unc + d->unc_len);
|
||||
|
||||
atomic_set(&d->stop, 1);
|
||||
atomic_set_release(&d->stop, 1);
|
||||
wake_up(&d->done);
|
||||
}
|
||||
return 0;
|
||||
@@ -1330,7 +1330,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
|
||||
if (crc->run_threads) {
|
||||
wait_event(crc->done, atomic_read(&crc->stop));
|
||||
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
||||
atomic_set(&crc->stop, 0);
|
||||
crc->run_threads = 0;
|
||||
}
|
||||
@@ -1366,7 +1366,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
pg = 0;
|
||||
}
|
||||
|
||||
atomic_set(&data[thr].ready, 1);
|
||||
atomic_set_release(&data[thr].ready, 1);
|
||||
wake_up(&data[thr].go);
|
||||
}
|
||||
|
||||
@@ -1385,7 +1385,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
|
||||
for (run_threads = thr, thr = 0; thr < run_threads; thr++) {
|
||||
wait_event(data[thr].done,
|
||||
atomic_read(&data[thr].stop));
|
||||
atomic_read_acquire(&data[thr].stop));
|
||||
atomic_set(&data[thr].stop, 0);
|
||||
|
||||
ret = data[thr].ret;
|
||||
@@ -1416,7 +1416,7 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
ret = snapshot_write_next(snapshot);
|
||||
if (ret <= 0) {
|
||||
crc->run_threads = thr + 1;
|
||||
atomic_set(&crc->ready, 1);
|
||||
atomic_set_release(&crc->ready, 1);
|
||||
wake_up(&crc->go);
|
||||
goto out_finish;
|
||||
}
|
||||
@@ -1424,13 +1424,13 @@ static int load_image_lzo(struct swap_map_handle *handle,
|
||||
}
|
||||
|
||||
crc->run_threads = thr;
|
||||
atomic_set(&crc->ready, 1);
|
||||
atomic_set_release(&crc->ready, 1);
|
||||
wake_up(&crc->go);
|
||||
}
|
||||
|
||||
out_finish:
|
||||
if (crc->run_threads) {
|
||||
wait_event(crc->done, atomic_read(&crc->stop));
|
||||
wait_event(crc->done, atomic_read_acquire(&crc->stop));
|
||||
atomic_set(&crc->stop, 0);
|
||||
}
|
||||
stop = ktime_get();
|
||||
|
||||
@@ -5312,6 +5312,8 @@ static int _sched_setscheduler(struct task_struct *p, int policy,
|
||||
* @policy: new policy.
|
||||
* @param: structure containing the new RT priority.
|
||||
*
|
||||
* Use sched_set_fifo(), read its comment.
|
||||
*
|
||||
* Return: 0 on success. An error code otherwise.
|
||||
*
|
||||
* NOTE that the task may be already dead.
|
||||
@@ -5354,6 +5356,51 @@ int sched_setscheduler_nocheck(struct task_struct *p, int policy,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sched_setscheduler_nocheck);
|
||||
|
||||
/*
|
||||
* SCHED_FIFO is a broken scheduler model; that is, it is fundamentally
|
||||
* incapable of resource management, which is the one thing an OS really should
|
||||
* be doing.
|
||||
*
|
||||
* This is of course the reason it is limited to privileged users only.
|
||||
*
|
||||
* Worse still; it is fundamentally impossible to compose static priority
|
||||
* workloads. You cannot take two correctly working static prio workloads
|
||||
* and smash them together and still expect them to work.
|
||||
*
|
||||
* For this reason 'all' FIFO tasks the kernel creates are basically at:
|
||||
*
|
||||
* MAX_RT_PRIO / 2
|
||||
*
|
||||
* The administrator _MUST_ configure the system, the kernel simply doesn't
|
||||
* know enough information to make a sensible choice.
|
||||
*/
|
||||
int sched_set_fifo(struct task_struct *p)
|
||||
{
|
||||
struct sched_param sp = { .sched_priority = MAX_RT_PRIO / 2 };
|
||||
return sched_setscheduler_nocheck(p, SCHED_FIFO, &sp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sched_set_fifo);
|
||||
|
||||
/*
|
||||
* For when you don't much care about FIFO, but want to be above SCHED_NORMAL.
|
||||
*/
|
||||
int sched_set_fifo_low(struct task_struct *p)
|
||||
{
|
||||
struct sched_param sp = { .sched_priority = 1 };
|
||||
return sched_setscheduler_nocheck(p, SCHED_FIFO, &sp);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sched_set_fifo_low);
|
||||
|
||||
int sched_set_normal(struct task_struct *p, int nice)
|
||||
{
|
||||
struct sched_attr attr = {
|
||||
.sched_policy = SCHED_NORMAL,
|
||||
.sched_nice = nice,
|
||||
};
|
||||
return sched_setattr_nocheck(p, &attr);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sched_set_normal);
|
||||
|
||||
static int
|
||||
do_sched_setscheduler(pid_t pid, int policy, struct sched_param __user *param)
|
||||
{
|
||||
|
||||
@@ -574,7 +574,12 @@ __tracing_map_insert(struct tracing_map *map, void *key, bool lookup_only)
|
||||
}
|
||||
|
||||
memcpy(elt->key, key, map->key_size);
|
||||
entry->val = elt;
|
||||
/*
|
||||
* Ensure the initialization is visible and
|
||||
* publish the elt.
|
||||
*/
|
||||
smp_wmb();
|
||||
WRITE_ONCE(entry->val, elt);
|
||||
atomic64_inc(&map->hits);
|
||||
|
||||
return entry->val;
|
||||
|
||||
@@ -120,12 +120,16 @@ static int vlan_changelink(struct net_device *dev, struct nlattr *tb[],
|
||||
}
|
||||
if (data[IFLA_VLAN_INGRESS_QOS]) {
|
||||
nla_for_each_nested(attr, data[IFLA_VLAN_INGRESS_QOS], rem) {
|
||||
if (nla_type(attr) != IFLA_VLAN_QOS_MAPPING)
|
||||
continue;
|
||||
m = nla_data(attr);
|
||||
vlan_dev_set_ingress_priority(dev, m->to, m->from);
|
||||
}
|
||||
}
|
||||
if (data[IFLA_VLAN_EGRESS_QOS]) {
|
||||
nla_for_each_nested(attr, data[IFLA_VLAN_EGRESS_QOS], rem) {
|
||||
if (nla_type(attr) != IFLA_VLAN_QOS_MAPPING)
|
||||
continue;
|
||||
m = nla_data(attr);
|
||||
err = vlan_dev_set_egress_priority(dev, m->from, m->to);
|
||||
if (err)
|
||||
|
||||
+1
-1
@@ -531,7 +531,7 @@ static int sock_setbindtodevice(struct sock *sk, char __user *optval,
|
||||
|
||||
/* Sorry... */
|
||||
ret = -EPERM;
|
||||
if (!ns_capable(net->user_ns, CAP_NET_RAW))
|
||||
if (sk->sk_bound_dev_if && !ns_capable(net->user_ns, CAP_NET_RAW))
|
||||
goto out;
|
||||
|
||||
ret = -EINVAL;
|
||||
|
||||
@@ -736,6 +736,7 @@ static void tcp_push(struct sock *sk, int flags, int mss_now,
|
||||
if (!test_bit(TSQ_THROTTLED, &sk->sk_tsq_flags)) {
|
||||
NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPAUTOCORKING);
|
||||
set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);
|
||||
smp_mb__after_atomic();
|
||||
}
|
||||
/* It is possible TX completion already happened
|
||||
* before we set TSQ_THROTTLED.
|
||||
|
||||
+16
-8
@@ -926,14 +926,15 @@ copy_uaddr:
|
||||
*/
|
||||
static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
{
|
||||
DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name);
|
||||
struct sock *sk = sock->sk;
|
||||
struct llc_sock *llc = llc_sk(sk);
|
||||
DECLARE_SOCKADDR(struct sockaddr_llc *, addr, msg->msg_name);
|
||||
int flags = msg->msg_flags;
|
||||
int noblock = flags & MSG_DONTWAIT;
|
||||
int rc = -EINVAL, copied = 0, hdrlen, hh_len;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct net_device *dev;
|
||||
size_t size = 0;
|
||||
int rc = -EINVAL, copied = 0, hdrlen;
|
||||
|
||||
dprintk("%s: sending from %02X to %02X\n", __func__,
|
||||
llc->laddr.lsap, llc->daddr.lsap);
|
||||
@@ -953,22 +954,29 @@ static int llc_ui_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
hdrlen = llc->dev->hard_header_len + llc_ui_header_len(sk, addr);
|
||||
dev = llc->dev;
|
||||
hh_len = LL_RESERVED_SPACE(dev);
|
||||
hdrlen = llc_ui_header_len(sk, addr);
|
||||
size = hdrlen + len;
|
||||
if (size > llc->dev->mtu)
|
||||
size = llc->dev->mtu;
|
||||
size = min_t(size_t, size, READ_ONCE(dev->mtu));
|
||||
copied = size - hdrlen;
|
||||
rc = -EINVAL;
|
||||
if (copied < 0)
|
||||
goto out;
|
||||
release_sock(sk);
|
||||
skb = sock_alloc_send_skb(sk, size, noblock, &rc);
|
||||
skb = sock_alloc_send_skb(sk, hh_len + size, noblock, &rc);
|
||||
lock_sock(sk);
|
||||
if (!skb)
|
||||
goto out;
|
||||
skb->dev = llc->dev;
|
||||
if (sock_flag(sk, SOCK_ZAPPED) ||
|
||||
llc->dev != dev ||
|
||||
hdrlen != llc_ui_header_len(sk, addr) ||
|
||||
hh_len != LL_RESERVED_SPACE(dev) ||
|
||||
size > READ_ONCE(dev->mtu))
|
||||
goto out;
|
||||
skb->dev = dev;
|
||||
skb->protocol = llc_proto_type(addr->sllc_arphrd);
|
||||
skb_reserve(skb, hdrlen);
|
||||
skb_reserve(skb, hh_len + hdrlen);
|
||||
rc = memcpy_from_msg(skb_put(skb, copied), msg, copied);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@@ -135,22 +135,15 @@ static struct packet_type llc_packet_type __read_mostly = {
|
||||
.func = llc_rcv,
|
||||
};
|
||||
|
||||
static struct packet_type llc_tr_packet_type __read_mostly = {
|
||||
.type = cpu_to_be16(ETH_P_TR_802_2),
|
||||
.func = llc_rcv,
|
||||
};
|
||||
|
||||
static int __init llc_init(void)
|
||||
{
|
||||
dev_add_pack(&llc_packet_type);
|
||||
dev_add_pack(&llc_tr_packet_type);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit llc_exit(void)
|
||||
{
|
||||
dev_remove_pack(&llc_packet_type);
|
||||
dev_remove_pack(&llc_tr_packet_type);
|
||||
}
|
||||
|
||||
module_init(llc_init);
|
||||
|
||||
+1
-1
@@ -385,7 +385,7 @@ static int rds_recv_track_latency(struct rds_sock *rs, char __user *optval,
|
||||
|
||||
rs->rs_rx_traces = trace.rx_traces;
|
||||
for (i = 0; i < rs->rs_rx_traces; i++) {
|
||||
if (trace.rx_trace_pos[i] > RDS_MSG_RX_DGRAM_TRACE_MAX) {
|
||||
if (trace.rx_trace_pos[i] >= RDS_MSG_RX_DGRAM_TRACE_MAX) {
|
||||
rs->rs_rx_traces = 0;
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
|
||||
}
|
||||
if (smc->conn.lgr && smc->conn.lgr->is_smcd &&
|
||||
(req->diag_ext & (1 << (SMC_DIAG_DMBINFO - 1))) &&
|
||||
!list_empty(&smc->conn.lgr->list)) {
|
||||
!list_empty(&smc->conn.lgr->list) && smc->conn.rmb_desc) {
|
||||
struct smc_connection *conn = &smc->conn;
|
||||
struct smcd_diag_dmbinfo dinfo;
|
||||
|
||||
|
||||
Reference in new issue
Block a user