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:
Michael Bestas committed 2024-04-20 01:21:32 +03:00
commit c2c8e4b330
54 files changed
+1390 -398

No files matched your search

+219
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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
+2 -2
View File
@@ -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;
+2 -2
View File
@@ -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 \
+3
View File
@@ -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
+11 -9
View File
@@ -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)
+2 -3
View File
@@ -1596,8 +1596,7 @@ 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);
set_page_dirty_lock(bvec->bv_page);
}
}
EXPORT_SYMBOL_GPL(bio_set_pages_dirty);
@@ -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;
}
+1
View File
@@ -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;
}
+44
View File
@@ -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);
+22 -14
View File
@@ -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 {
len = data_avail;
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:
+2
View File
@@ -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 },
+3
View File
@@ -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;
}
+8 -2
View File
@@ -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);
}
}
+1
View File
@@ -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);
}
+6 -2
View File
@@ -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;
+17
View File
@@ -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.
-45
View File
@@ -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
View File
@@ -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);
+125 -100
View File
@@ -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,61 +307,55 @@ 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;
if (nrpages_out == 1 && !rq->inplace_io) {
DBG_BUGON(!*rq->out);
dst = kmap_atomic(*rq->out);
dst_maptype = 0;
goto dstmap_out;
}
/* 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);
dst_maptype = 0;
goto dstmap_out;
}
/*
* For the case of small output size (especially much less
* than PAGE_SIZE), memcpy the decompressed data rather than
* compressed data is preferred.
*/
if (rq->outputsize <= PAGE_SIZE * 7 / 8) {
dst = erofs_get_pcpubuf(1);
if (IS_ERR(dst))
return PTR_ERR(dst);
/*
* For the case of small output size (especially much less
* than PAGE_SIZE), memcpy the decompressed data rather than
* compressed data is preferred.
*/
if (rq->outputsize <= PAGE_SIZE * 7 / 8) {
dst = erofs_get_pcpubuf(1);
if (IS_ERR(dst))
return PTR_ERR(dst);
rq->inplace_io = false;
ret = alg->decompress(rq, dst, NULL);
if (!ret)
copy_from_pcpubuf(rq->out, dst, rq->pageofs_out,
rq->outputsize);
rq->inplace_io = false;
ret = alg->decompress(rq, dst);
if (!ret)
copy_from_pcpubuf(rq->out, dst, rq->pageofs_out,
rq->outputsize);
erofs_put_pcpubuf(dst);
return ret;
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);
-1
View File
@@ -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
+15
View File
@@ -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);
+185 -47
View File
@@ -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)
z_erofs_destroy_pcluster_pool();
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
View File
@@ -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;
};
+6 -3
View File
@@ -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
+1
View File
@@ -2,6 +2,7 @@
config EXFAT_FS
tristate "exFAT filesystem support"
select BUFFER_HEAD
select NLS
select LEGACY_DIRECT_IO
help
+38 -51
View File
@@ -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
View File
@@ -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
View File
@@ -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
+9 -8
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+10
View File
@@ -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;
+133 -29
View File
@@ -378,14 +378,20 @@ static int exfat_find_empty_entry(struct inode *inode,
if (exfat_check_max_dentries(inode))
return -ENOSPC;
/* 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);
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;
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,13 +572,16 @@ 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->start_clu = start_clu;
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
View File
@@ -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
View File
@@ -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
+3
View File
@@ -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))
+37
View File
@@ -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
+3
View File
@@ -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);
+1
View File
@@ -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 */
+92
View File
@@ -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 */
+2 -4
View File
@@ -262,8 +262,7 @@ 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);
memcpy(sa, eth_hdr(skb)->h_source, ETH_ALEN);
}
/**
@@ -275,8 +274,7 @@ 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);
memcpy(da, eth_hdr(skb)->h_dest, ETH_ALEN);
}
/**
+1
View File
@@ -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
View File
@@ -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();
+47
View File
@@ -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)
{
+6 -1
View File
@@ -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;
+4
View File
@@ -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
View File
@@ -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;
+1
View File
@@ -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
View File
@@ -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;
-7
View File
@@ -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
View File
@@ -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
View File
@@ -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;