Merge 2be86a8c57 ("Merge tag 'hwmon-for-v7.1-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging") into android-mainline
Steps on the way to v7.1-rc5 Change-Id: If3e7b89bb02eea9f0da965fc3fb9e9a50e80b939 Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
commit
d0fd6f5393
56 files changed
+772
-293
No files matched your search
+2
-1
@@ -18955,7 +18955,8 @@ F: drivers/hid/hid-nintendo*
|
||||
|
||||
NIOS2 ARCHITECTURE
|
||||
M: Dinh Nguyen <dinguyen@kernel.org>
|
||||
S: Maintained
|
||||
M: Simon Schuster <schuster.simon@siemens-energy.com>
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/dinguyen/linux.git
|
||||
F: arch/nios2/
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ static inline unsigned long efi_get_kimg_min_align(void)
|
||||
return SZ_2M;
|
||||
}
|
||||
|
||||
#define EFI_KIMG_PREFERRED_ADDRESS PHYSADDR(VMLINUX_LOAD_ADDRESS)
|
||||
unsigned long efi_get_kimg_kaslr_address(void);
|
||||
|
||||
#define EFI_KIMG_PREFERRED_ADDRESS efi_get_kimg_kaslr_address()
|
||||
|
||||
#endif /* _ASM_LOONGARCH_EFI_H */
|
||||
@@ -4,6 +4,12 @@
|
||||
|
||||
#ifdef CONFIG_PARAVIRT
|
||||
|
||||
#include <linux/jump_label.h>
|
||||
|
||||
DECLARE_STATIC_KEY_FALSE(virt_preempt_key);
|
||||
DECLARE_STATIC_KEY_FALSE(virt_spin_lock_key);
|
||||
DECLARE_PER_CPU(struct kvm_steal_time, steal_time);
|
||||
|
||||
int __init pv_ipi_init(void);
|
||||
int __init pv_time_init(void);
|
||||
int __init pv_spinlock_init(void);
|
||||
|
||||
@@ -3,12 +3,9 @@
|
||||
#define _ASM_LOONGARCH_QSPINLOCK_H
|
||||
|
||||
#include <asm/kvm_para.h>
|
||||
#include <linux/jump_label.h>
|
||||
#include <asm/paravirt.h>
|
||||
|
||||
#ifdef CONFIG_PARAVIRT
|
||||
DECLARE_STATIC_KEY_FALSE(virt_preempt_key);
|
||||
DECLARE_STATIC_KEY_FALSE(virt_spin_lock_key);
|
||||
DECLARE_PER_CPU(struct kvm_steal_time, steal_time);
|
||||
|
||||
#define virt_spin_lock virt_spin_lock
|
||||
|
||||
|
||||
@@ -60,16 +60,18 @@ NOKPROBE_SYMBOL(arch_prepare_kprobe);
|
||||
/* Install breakpoint in text */
|
||||
void arch_arm_kprobe(struct kprobe *p)
|
||||
{
|
||||
*p->addr = KPROBE_BP_INSN;
|
||||
flush_insn_slot(p);
|
||||
u32 insn = KPROBE_BP_INSN;
|
||||
|
||||
larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE);
|
||||
}
|
||||
NOKPROBE_SYMBOL(arch_arm_kprobe);
|
||||
|
||||
/* Remove breakpoint from text */
|
||||
void arch_disarm_kprobe(struct kprobe *p)
|
||||
{
|
||||
*p->addr = p->opcode;
|
||||
flush_insn_slot(p);
|
||||
u32 insn = p->opcode;
|
||||
|
||||
larch_insn_text_copy(p->addr, &insn, LOONGARCH_INSN_SIZE);
|
||||
}
|
||||
NOKPROBE_SYMBOL(arch_disarm_kprobe);
|
||||
|
||||
@@ -184,16 +186,16 @@ static bool reenter_kprobe(struct kprobe *p, struct pt_regs *regs,
|
||||
struct kprobe_ctlblk *kcb)
|
||||
{
|
||||
switch (kcb->kprobe_status) {
|
||||
case KPROBE_HIT_SS:
|
||||
case KPROBE_HIT_SSDONE:
|
||||
case KPROBE_HIT_ACTIVE:
|
||||
kprobes_inc_nmissed_count(p);
|
||||
setup_singlestep(p, regs, kcb, 1);
|
||||
break;
|
||||
case KPROBE_HIT_SS:
|
||||
case KPROBE_REENTER:
|
||||
pr_warn("Failed to recover from reentered kprobes.\n");
|
||||
dump_kprobe(p);
|
||||
WARN_ON_ONCE(1);
|
||||
BUG();
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
|
||||
@@ -134,11 +134,23 @@ early_param("nokaslr", nokaslr);
|
||||
|
||||
#define KASLR_DISABLED_MESSAGE "KASLR is disabled by %s in %s cmdline.\n"
|
||||
|
||||
/*
|
||||
* Note: strictly-defined KASLR means the kernel's final runtime address
|
||||
* has a random offset from the kernel's load address, which is implemented
|
||||
* in relocate.c; broadly-defined KALSR means the kernel's final runtime
|
||||
* address has a random offset from the kernel's link address (a.k.a.
|
||||
* VMLINUX_LOAD_ADDRESS), which also include the efistlub implementation,
|
||||
* kexec_file implementation and QEMU direct kernel boot. kaslr_disabled()
|
||||
* return true only means strictly-defined KASLR is disabled.
|
||||
*/
|
||||
static inline __init bool kaslr_disabled(void)
|
||||
{
|
||||
char *str;
|
||||
const char *builtin_cmdline = CONFIG_CMDLINE;
|
||||
|
||||
if (kaslr_offset())
|
||||
return true; /* KASLR is performed during early boot. */
|
||||
|
||||
str = strstr(builtin_cmdline, "nokaslr");
|
||||
if (str == builtin_cmdline || (str > builtin_cmdline && *(str - 1) == ' ')) {
|
||||
pr_info(KASLR_DISABLED_MESSAGE, "\'nokaslr\'", "built-in");
|
||||
@@ -210,14 +222,52 @@ static inline void __init *determine_relocation_address(void)
|
||||
return RELOCATED_KASLR(destination);
|
||||
}
|
||||
|
||||
static unsigned long __init determine_initrd_address(unsigned long *size)
|
||||
{
|
||||
unsigned long start = 0;
|
||||
unsigned long key_length;
|
||||
char *p, *endp, *key = "initrd=";
|
||||
|
||||
key_length = strlen(key);
|
||||
p = strstr(boot_command_line, key);
|
||||
|
||||
if (!p) {
|
||||
key = "initrdmem=";
|
||||
key_length = strlen(key);
|
||||
p = strstr(boot_command_line, key);
|
||||
}
|
||||
|
||||
if (p == boot_command_line || (p > boot_command_line && *(p - 1) == ' ')) {
|
||||
p += key_length;
|
||||
start = memparse(p, &endp);
|
||||
if (*endp == ',')
|
||||
*size = memparse(endp + 1, NULL);
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
static inline int __init relocation_addr_valid(void *location_new)
|
||||
{
|
||||
unsigned long kernel_start, kernel_size;
|
||||
unsigned long initrd_start, initrd_size = 0;
|
||||
|
||||
if ((unsigned long)location_new & 0x00000ffff)
|
||||
return 0; /* Inappropriately aligned new location */
|
||||
|
||||
if ((unsigned long)location_new < (unsigned long)_end)
|
||||
return 0; /* New location overlaps original kernel */
|
||||
|
||||
initrd_start = determine_initrd_address(&initrd_size);
|
||||
if (initrd_start && initrd_size) {
|
||||
kernel_start = PHYSADDR(location_new);
|
||||
kernel_size = (unsigned long)_end - (unsigned long)_text;
|
||||
|
||||
if (kernel_start < (initrd_start + initrd_size) &&
|
||||
initrd_start < (kernel_start + kernel_size))
|
||||
return 0; /* initrd/initramfs overlaps kernel */
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -123,11 +123,7 @@ void arch_remove_memory(u64 start, u64 size, struct vmem_altmap *altmap)
|
||||
{
|
||||
unsigned long start_pfn = start >> PAGE_SHIFT;
|
||||
unsigned long nr_pages = size >> PAGE_SHIFT;
|
||||
struct page *page = pfn_to_page(start_pfn);
|
||||
|
||||
/* With altmap the first mapped page is offset from @start */
|
||||
if (altmap)
|
||||
page += vmem_altmap_offset(altmap);
|
||||
__remove_pages(start_pfn, nr_pages, altmap);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -12,4 +12,6 @@
|
||||
#define __ALIGN .align 4
|
||||
#define __ALIGN_STR ".align 4"
|
||||
|
||||
#define _THIS_IP_ ({ unsigned long __ip; asm volatile("nextpc %0" : "=r" (__ip)); __ip; })
|
||||
|
||||
#endif
|
||||
+15
-1
@@ -606,6 +606,12 @@ static void platform_device_release(struct device *dev)
|
||||
kfree(pa);
|
||||
}
|
||||
|
||||
static void platform_device_release_full(struct device *dev)
|
||||
{
|
||||
device_remove_software_node(dev);
|
||||
platform_device_release(dev);
|
||||
}
|
||||
|
||||
/**
|
||||
* platform_device_alloc - create a platform device
|
||||
* @name: base name of the device we're adding
|
||||
@@ -848,7 +854,13 @@ struct platform_device *platform_device_register_full(const struct platform_devi
|
||||
int ret;
|
||||
struct platform_device *pdev;
|
||||
|
||||
if (pdevinfo->swnode && pdevinfo->properties)
|
||||
/*
|
||||
* Only one software node per device is allowed. Make sure we don't
|
||||
* accept or create two.
|
||||
*/
|
||||
if ((pdevinfo->swnode && pdevinfo->properties) ||
|
||||
(pdevinfo->swnode && is_software_node(pdevinfo->fwnode)) ||
|
||||
(pdevinfo->properties && is_software_node(pdevinfo->fwnode)))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
|
||||
@@ -878,6 +890,8 @@ struct platform_device *platform_device_register_full(const struct platform_devi
|
||||
ret = device_add_software_node(&pdev->dev, pdevinfo->swnode);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
pdev->dev.release = platform_device_release_full;
|
||||
} else if (pdevinfo->properties) {
|
||||
ret = device_create_managed_software_node(&pdev->dev,
|
||||
pdevinfo->properties, NULL);
|
||||
|
||||
@@ -79,6 +79,10 @@ efi_status_t efi_parse_options(char const *cmdline)
|
||||
efi_noinitrd = true;
|
||||
} else if (IS_ENABLED(CONFIG_X86_64) && !strcmp(param, "no5lvl")) {
|
||||
efi_no5lvl = true;
|
||||
} else if (IS_ENABLED(CONFIG_LOONGARCH) &&
|
||||
IS_ENABLED(CONFIG_HIBERNATION) &&
|
||||
!strcmp(param, "resume") && val) {
|
||||
efi_nokaslr = true; /* LoongArch can't KASLR for hibernation */
|
||||
} else if (IS_ENABLED(CONFIG_ARCH_HAS_MEM_ENCRYPT) &&
|
||||
!strcmp(param, "mem_encrypt") && val) {
|
||||
if (parse_option_str(val, "on"))
|
||||
|
||||
@@ -23,6 +23,22 @@ void efi_cache_sync_image(unsigned long image_base, unsigned long alloc_size)
|
||||
asm volatile ("ibar 0" ::: "memory");
|
||||
}
|
||||
|
||||
unsigned long efi_get_kimg_kaslr_address(void)
|
||||
{
|
||||
unsigned int random_offset = 0;
|
||||
|
||||
#ifdef CONFIG_RANDOMIZE_BASE
|
||||
if (!efi_nokaslr) {
|
||||
efi_get_random_bytes(sizeof(random_offset), (u8 *)&random_offset);
|
||||
random_offset ^= (random_get_entropy() << 16);
|
||||
random_offset &= (CONFIG_RANDOMIZE_BASE_MAX_OFFSET - 1);
|
||||
random_offset = ALIGN(random_offset + SZ_64K, SZ_64K);
|
||||
}
|
||||
#endif
|
||||
|
||||
return PHYSADDR(VMLINUX_LOAD_ADDRESS) + random_offset;
|
||||
}
|
||||
|
||||
struct exit_boot_struct {
|
||||
efi_memory_desc_t *runtime_map;
|
||||
int runtime_entry_count;
|
||||
|
||||
@@ -102,7 +102,7 @@ out_unlock:
|
||||
}
|
||||
|
||||
static bool
|
||||
purge(struct drm_gem_object *obj, struct ww_acquire_ctx *)
|
||||
purge(struct drm_gem_object *obj, struct ww_acquire_ctx *unused)
|
||||
{
|
||||
if (!is_purgeable(to_msm_bo(obj)))
|
||||
return false;
|
||||
@@ -114,7 +114,7 @@ purge(struct drm_gem_object *obj, struct ww_acquire_ctx *)
|
||||
}
|
||||
|
||||
static bool
|
||||
evict(struct drm_gem_object *obj, struct ww_acquire_ctx *)
|
||||
evict(struct drm_gem_object *obj, struct ww_acquire_ctx *unused)
|
||||
{
|
||||
if (is_unevictable(to_msm_bo(obj)))
|
||||
return false;
|
||||
|
||||
@@ -519,8 +519,8 @@ static int lenovo_ec_probe(struct platform_device *pdev)
|
||||
if (!ec_data)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!request_region(IO_REGION_START, IO_REGION_LENGTH, "LNV-WKS")) {
|
||||
pr_err(":request fail\n");
|
||||
if (!devm_request_region(dev, IO_REGION_START, IO_REGION_LENGTH, "LNV-WKS")) {
|
||||
dev_err(dev, "Failed to request I/O region\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
@@ -537,13 +537,11 @@ static int lenovo_ec_probe(struct platform_device *pdev)
|
||||
outw_p(MCHP_SING_IDX, MCHP_EMI0_EC_ADDRESS);
|
||||
mutex_unlock(&ec_data->mec_mutex);
|
||||
|
||||
if ((inb_p(MCHP_EMI0_EC_DATA_BYTE0) != 'M') &&
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE1) != 'C') &&
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE2) != 'H') &&
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE3) != 'P')) {
|
||||
release_region(IO_REGION_START, IO_REGION_LENGTH);
|
||||
if ((inb_p(MCHP_EMI0_EC_DATA_BYTE0) != 'M') ||
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE1) != 'C') ||
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE2) != 'H') ||
|
||||
(inb_p(MCHP_EMI0_EC_DATA_BYTE3) != 'P'))
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
dmi_id = dmi_first_match(thinkstation_dmi_table);
|
||||
|
||||
@@ -577,7 +575,6 @@ static int lenovo_ec_probe(struct platform_device *pdev)
|
||||
lenovo_ec_chip_info.info = lenovo_ec_hwmon_info_p8;
|
||||
break;
|
||||
default:
|
||||
release_region(IO_REGION_START, IO_REGION_LENGTH);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
@@ -606,10 +603,8 @@ static int __init lenovo_ec_init(void)
|
||||
platform_create_bundle(&lenovo_ec_sensors_platform_driver,
|
||||
lenovo_ec_probe, NULL, 0, NULL, 0);
|
||||
|
||||
if (IS_ERR(lenovo_ec_sensors_platform_device)) {
|
||||
release_region(IO_REGION_START, IO_REGION_LENGTH);
|
||||
if (IS_ERR(lenovo_ec_sensors_platform_device))
|
||||
return PTR_ERR(lenovo_ec_sensors_platform_device);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -617,7 +612,6 @@ module_init(lenovo_ec_init);
|
||||
|
||||
static void __exit lenovo_ec_exit(void)
|
||||
{
|
||||
release_region(IO_REGION_START, IO_REGION_LENGTH);
|
||||
platform_device_unregister(lenovo_ec_sensors_platform_device);
|
||||
platform_driver_unregister(&lenovo_ec_sensors_platform_driver);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
|
||||
#define ADM1266_BLACKBOX_OFFSET 0
|
||||
#define ADM1266_BLACKBOX_SIZE 64
|
||||
#define ADM1266_BLACKBOX_MAX_RECORDS 32
|
||||
|
||||
#define ADM1266_PMBUS_BLOCK_MAX 255
|
||||
|
||||
@@ -60,7 +61,7 @@ struct adm1266_data {
|
||||
u8 *dev_mem;
|
||||
struct mutex buf_mutex;
|
||||
u8 write_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned;
|
||||
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 1] ____cacheline_aligned;
|
||||
u8 read_buf[ADM1266_PMBUS_BLOCK_MAX + 2] ____cacheline_aligned;
|
||||
};
|
||||
|
||||
static const struct nvmem_cell_info adm1266_nvmem_cells[] = {
|
||||
@@ -172,9 +173,13 @@ static int adm1266_gpio_get(struct gpio_chip *chip, unsigned int offset)
|
||||
else
|
||||
pmbus_cmd = ADM1266_PDIO_STATUS;
|
||||
|
||||
guard(pmbus_lock)(data->client);
|
||||
|
||||
ret = i2c_smbus_read_block_data(data->client, pmbus_cmd, read_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < 2)
|
||||
return -EIO;
|
||||
|
||||
pins_status = read_buf[0] + (read_buf[1] << 8);
|
||||
if (offset < ADM1266_GPIO_NR)
|
||||
@@ -192,9 +197,13 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
|
||||
unsigned int gpio_nr;
|
||||
int ret;
|
||||
|
||||
guard(pmbus_lock)(data->client);
|
||||
|
||||
ret = i2c_smbus_read_block_data(data->client, ADM1266_GPIO_STATUS, read_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < 2)
|
||||
return -EIO;
|
||||
|
||||
status = read_buf[0] + (read_buf[1] << 8);
|
||||
|
||||
@@ -207,11 +216,12 @@ static int adm1266_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask
|
||||
ret = i2c_smbus_read_block_data(data->client, ADM1266_PDIO_STATUS, read_buf);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret < 2)
|
||||
return -EIO;
|
||||
|
||||
status = read_buf[0] + (read_buf[1] << 8);
|
||||
|
||||
*bits = 0;
|
||||
for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_STATUS) {
|
||||
for_each_set_bit_from(gpio_nr, mask, ADM1266_GPIO_NR + ADM1266_PDIO_NR) {
|
||||
if (test_bit(gpio_nr - ADM1266_GPIO_NR, &status))
|
||||
set_bit(gpio_nr, bits);
|
||||
}
|
||||
@@ -230,6 +240,8 @@ static void adm1266_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
guard(pmbus_lock)(data->client);
|
||||
|
||||
for (i = 0; i < ADM1266_GPIO_NR; i++) {
|
||||
write_cmd = adm1266_gpio_mapping[i][1];
|
||||
ret = adm1266_pmbus_block_xfer(data, ADM1266_GPIO_CONFIG, 1, &write_cmd, read_buf);
|
||||
@@ -290,8 +302,9 @@ static int adm1266_config_gpio(struct adm1266_data *data)
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(data->gpio_names); i++) {
|
||||
gpio_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "adm1266-%x-%s",
|
||||
data->client->addr, adm1266_names[i]);
|
||||
gpio_name = devm_kasprintf(&data->client->dev, GFP_KERNEL, "adm1266-%d-%x-%s",
|
||||
data->client->adapter->nr, data->client->addr,
|
||||
adm1266_names[i]);
|
||||
if (!gpio_name)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -322,6 +335,7 @@ static int adm1266_state_read(struct seq_file *s, void *pdata)
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int ret;
|
||||
|
||||
guard(pmbus_lock)(client);
|
||||
ret = i2c_smbus_read_word_data(client, ADM1266_READ_STATE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
@@ -347,9 +361,10 @@ static void adm1266_init_debugfs(struct adm1266_data *data)
|
||||
|
||||
static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
|
||||
{
|
||||
u8 record[ADM1266_PMBUS_BLOCK_MAX];
|
||||
int record_count;
|
||||
char index;
|
||||
u8 buf[5];
|
||||
u8 buf[I2C_SMBUS_BLOCK_MAX];
|
||||
int ret;
|
||||
|
||||
ret = i2c_smbus_read_block_data(data->client, ADM1266_BLACKBOX_INFO, buf);
|
||||
@@ -360,15 +375,18 @@ static int adm1266_nvmem_read_blackbox(struct adm1266_data *data, u8 *read_buff)
|
||||
return -EIO;
|
||||
|
||||
record_count = buf[3];
|
||||
if (record_count > ADM1266_BLACKBOX_MAX_RECORDS)
|
||||
return -EIO;
|
||||
|
||||
for (index = 0; index < record_count; index++) {
|
||||
ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, read_buff);
|
||||
ret = adm1266_pmbus_block_xfer(data, ADM1266_READ_BLACKBOX, 1, &index, record);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret != ADM1266_BLACKBOX_SIZE)
|
||||
return -EIO;
|
||||
|
||||
memcpy(read_buff, record, ADM1266_BLACKBOX_SIZE);
|
||||
read_buff += ADM1266_BLACKBOX_SIZE;
|
||||
}
|
||||
|
||||
@@ -383,6 +401,8 @@ static int adm1266_nvmem_read(void *priv, unsigned int offset, void *val, size_t
|
||||
if (offset + bytes > data->nvmem_config.size)
|
||||
return -EINVAL;
|
||||
|
||||
guard(pmbus_lock)(data->client);
|
||||
|
||||
if (offset == 0) {
|
||||
memset(data->dev_mem, 0, data->nvmem_config.size);
|
||||
|
||||
@@ -432,7 +452,7 @@ static int adm1266_set_rtc(struct adm1266_data *data)
|
||||
char write_buf[6];
|
||||
int i;
|
||||
|
||||
kt = ktime_get_seconds();
|
||||
kt = ktime_get_real_seconds();
|
||||
|
||||
memset(write_buf, 0, sizeof(write_buf));
|
||||
|
||||
@@ -462,20 +482,20 @@ static int adm1266_probe(struct i2c_client *client)
|
||||
crc8_populate_msb(pmbus_crc_table, 0x7);
|
||||
mutex_init(&data->buf_mutex);
|
||||
|
||||
ret = adm1266_config_gpio(data);
|
||||
ret = adm1266_set_rtc(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = adm1266_set_rtc(data);
|
||||
if (ret < 0)
|
||||
ret = pmbus_do_probe(client, &data->info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = adm1266_config_nvmem(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = pmbus_do_probe(client, &data->info);
|
||||
if (ret)
|
||||
ret = adm1266_config_gpio(data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
adm1266_init_debugfs(data);
|
||||
|
||||
@@ -589,25 +589,22 @@ static int tegra_i2c_mutex_lock(struct tegra_i2c_dev *i2c_dev)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
|
||||
static void tegra_i2c_mutex_unlock(struct tegra_i2c_dev *i2c_dev)
|
||||
{
|
||||
unsigned int reg = i2c_dev->hw->regs->sw_mutex;
|
||||
u32 val, id;
|
||||
|
||||
if (!i2c_dev->hw->has_mutex)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
val = readl(i2c_dev->base + reg);
|
||||
|
||||
id = FIELD_GET(I2C_SW_MUTEX_GRANT, val);
|
||||
if (id && id != I2C_SW_MUTEX_ID_CCPLEX) {
|
||||
dev_warn(i2c_dev->dev, "unable to unlock mutex, mutex is owned by: %u\n", id);
|
||||
return -EPERM;
|
||||
}
|
||||
if (WARN(id && id != I2C_SW_MUTEX_ID_CCPLEX,
|
||||
"unable to unlock mutex, mutex is owned by: %u\n", id))
|
||||
return;
|
||||
|
||||
writel(0, i2c_dev->base + reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tegra_i2c_mask_irq(struct tegra_i2c_dev *i2c_dev, u32 mask)
|
||||
@@ -1666,8 +1663,10 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
}
|
||||
|
||||
ret = tegra_i2c_mutex_lock(i2c_dev);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
pm_runtime_put(i2c_dev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
enum msg_end_type end_type = MSG_END_STOP;
|
||||
@@ -1698,7 +1697,7 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
|
||||
break;
|
||||
}
|
||||
|
||||
ret = tegra_i2c_mutex_unlock(i2c_dev);
|
||||
tegra_i2c_mutex_unlock(i2c_dev);
|
||||
pm_runtime_put(i2c_dev->dev);
|
||||
|
||||
return ret ?: i;
|
||||
|
||||
@@ -353,6 +353,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter *adapter, u16 addr,
|
||||
&& size != I2C_SMBUS_I2C_BLOCK_DATA);
|
||||
|
||||
msgbuf0[0] = command;
|
||||
msgbuf1[0] = 0;
|
||||
switch (size) {
|
||||
case I2C_SMBUS_QUICK:
|
||||
msg[0].len = 0;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/dma-resv.h>
|
||||
#include "uverbs.h"
|
||||
#include "core_priv.h"
|
||||
#include "rdma_core.h"
|
||||
|
||||
MODULE_IMPORT_NS("DMA_BUF");
|
||||
|
||||
@@ -416,3 +417,89 @@ struct ib_device *rdma_udata_to_dev(struct ib_udata *udata)
|
||||
}
|
||||
EXPORT_SYMBOL(rdma_udata_to_dev);
|
||||
|
||||
#if IS_ENABLED(CONFIG_INFINIBAND_USER_ACCESS)
|
||||
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
|
||||
{
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
rdma_udata_to_uverbs_attr_bundle(udata);
|
||||
|
||||
lockdep_assert_held(&bundle->ufile->device->disassociate_srcu);
|
||||
|
||||
return srcu_dereference(bundle->method_elm->handler,
|
||||
&bundle->ufile->device->disassociate_srcu);
|
||||
}
|
||||
|
||||
int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
|
||||
size_t kernel_size, size_t minimum_size)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (udata->inlen < minimum_size) {
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata too small (%zu < %zu) for ioctl %ps called by %pSR\n",
|
||||
udata->inlen, minimum_size,
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = copy_struct_from_user(req, kernel_size, udata->inbuf,
|
||||
udata->inlen);
|
||||
if (err) {
|
||||
if (err == -E2BIG) {
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata not zero from %zu -> %zu for ioctl %ps called by %pSR\n",
|
||||
minimum_size, udata->inlen,
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata EFAULT for ioctl %ps called by %pSR\n",
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_copy_validate_udata_in);
|
||||
|
||||
int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
|
||||
u64 valid_cm)
|
||||
{
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata has unsupported comp_mask %llx & ~%llx = %llx for ioctl %ps called by %pSR\n",
|
||||
req_cm, valid_cm, req_cm & ~valid_cm,
|
||||
uverbs_get_handler_fn(udata), __builtin_return_address(0));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_copy_validate_udata_cm_fail);
|
||||
|
||||
int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len)
|
||||
{
|
||||
size_t copy_len;
|
||||
|
||||
/* 0 length copy_len is a NOP for copy_to_user() and doesn't fail. */
|
||||
copy_len = min(len, udata->outlen);
|
||||
if (copy_to_user(udata->outbuf, src, copy_len))
|
||||
goto err_fault;
|
||||
if (copy_len < udata->outlen) {
|
||||
if (clear_user(udata->outbuf + copy_len,
|
||||
udata->outlen - copy_len))
|
||||
goto err_fault;
|
||||
}
|
||||
return 0;
|
||||
err_fault:
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver out udata has EFAULT (%zu into %zu) for ioctl %ps called by %pSR\n",
|
||||
len, udata->outlen, uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_respond_udata);
|
||||
#endif
|
||||
@@ -51,6 +51,7 @@
|
||||
* a controlled QKEY.
|
||||
*/
|
||||
static bool privileged_qkey;
|
||||
static DEFINE_MUTEX(nldev_dellink_mutex);
|
||||
|
||||
typedef int (*res_fill_func_t)(struct sk_buff*, bool,
|
||||
struct rdma_restrack_entry*, uint32_t);
|
||||
@@ -1846,7 +1847,9 @@ static int nldev_dellink(struct sk_buff *skb, struct nlmsghdr *nlh,
|
||||
* implicitly scoped to the driver supporting dynamic link deletion like RXE.
|
||||
*/
|
||||
if (device->link_ops && device->link_ops->dellink) {
|
||||
mutex_lock(&nldev_dellink_mutex);
|
||||
err = device->link_ops->dellink(device);
|
||||
mutex_unlock(&nldev_dellink_mutex);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -229,6 +229,40 @@ int uverbs_dealloc_mw(struct ib_mw *mw);
|
||||
void ib_uverbs_detach_umcast(struct ib_qp *qp,
|
||||
struct ib_uqp_object *uobj);
|
||||
|
||||
struct bundle_alloc_head {
|
||||
struct_group_tagged(bundle_alloc_head_hdr, hdr,
|
||||
struct bundle_alloc_head *next;
|
||||
);
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
struct bundle_priv {
|
||||
/* Must be first */
|
||||
struct bundle_alloc_head_hdr alloc_head;
|
||||
struct bundle_alloc_head *allocated_mem;
|
||||
size_t internal_avail;
|
||||
size_t internal_used;
|
||||
|
||||
struct radix_tree_root *radix;
|
||||
void __rcu **radix_slots;
|
||||
unsigned long radix_slots_len;
|
||||
u32 method_key;
|
||||
|
||||
struct ib_uverbs_attr __user *user_attrs;
|
||||
struct ib_uverbs_attr *uattrs;
|
||||
|
||||
DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
|
||||
DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
|
||||
DECLARE_BITMAP(uobj_hw_obj_valid, UVERBS_API_ATTR_BKEY_LEN);
|
||||
|
||||
/*
|
||||
* Must be last. bundle ends in a flex array which overlaps
|
||||
* internal_buffer.
|
||||
*/
|
||||
struct uverbs_attr_bundle_hdr bundle;
|
||||
u64 internal_buffer[32];
|
||||
};
|
||||
|
||||
long ib_uverbs_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
|
||||
|
||||
struct ib_uverbs_flow_spec {
|
||||
|
||||
@@ -35,54 +35,6 @@
|
||||
#include "rdma_core.h"
|
||||
#include "uverbs.h"
|
||||
|
||||
struct bundle_alloc_head {
|
||||
struct_group_tagged(bundle_alloc_head_hdr, hdr,
|
||||
struct bundle_alloc_head *next;
|
||||
);
|
||||
u8 data[];
|
||||
};
|
||||
|
||||
struct bundle_priv {
|
||||
/* Must be first */
|
||||
struct bundle_alloc_head_hdr alloc_head;
|
||||
struct bundle_alloc_head *allocated_mem;
|
||||
size_t internal_avail;
|
||||
size_t internal_used;
|
||||
|
||||
struct radix_tree_root *radix;
|
||||
const struct uverbs_api_ioctl_method *method_elm;
|
||||
void __rcu **radix_slots;
|
||||
unsigned long radix_slots_len;
|
||||
u32 method_key;
|
||||
|
||||
struct ib_uverbs_attr __user *user_attrs;
|
||||
struct ib_uverbs_attr *uattrs;
|
||||
|
||||
DECLARE_BITMAP(uobj_finalize, UVERBS_API_ATTR_BKEY_LEN);
|
||||
DECLARE_BITMAP(spec_finalize, UVERBS_API_ATTR_BKEY_LEN);
|
||||
DECLARE_BITMAP(uobj_hw_obj_valid, UVERBS_API_ATTR_BKEY_LEN);
|
||||
|
||||
/*
|
||||
* Must be last. bundle ends in a flex array which overlaps
|
||||
* internal_buffer.
|
||||
*/
|
||||
struct uverbs_attr_bundle_hdr bundle;
|
||||
u64 internal_buffer[32];
|
||||
};
|
||||
|
||||
uverbs_api_ioctl_handler_fn uverbs_get_handler_fn(struct ib_udata *udata)
|
||||
{
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
rdma_udata_to_uverbs_attr_bundle(udata);
|
||||
struct bundle_priv *pbundle =
|
||||
container_of(&bundle->hdr, struct bundle_priv, bundle);
|
||||
|
||||
lockdep_assert_held(&bundle->ufile->device->disassociate_srcu);
|
||||
|
||||
return srcu_dereference(pbundle->method_elm->handler,
|
||||
&bundle->ufile->device->disassociate_srcu);
|
||||
}
|
||||
|
||||
/*
|
||||
* Each method has an absolute minimum amount of memory it needs to allocate,
|
||||
* precompute that amount and determine if the onstack memory can be used or
|
||||
@@ -445,13 +397,13 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr);
|
||||
size_t uattrs_size = array_size(sizeof(*pbundle->uattrs), num_attrs);
|
||||
unsigned int destroy_bkey = pbundle->method_elm->destroy_bkey;
|
||||
unsigned int destroy_bkey = bundle->method_elm->destroy_bkey;
|
||||
unsigned int i;
|
||||
int ret;
|
||||
|
||||
/* See uverbs_disassociate_api() */
|
||||
handler = srcu_dereference(
|
||||
pbundle->method_elm->handler,
|
||||
bundle->method_elm->handler,
|
||||
&pbundle->bundle.ufile->device->disassociate_srcu);
|
||||
if (!handler)
|
||||
return -EIO;
|
||||
@@ -469,12 +421,12 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
|
||||
}
|
||||
|
||||
/* User space did not provide all the mandatory attributes */
|
||||
if (unlikely(!bitmap_subset(pbundle->method_elm->attr_mandatory,
|
||||
if (unlikely(!bitmap_subset(bundle->method_elm->attr_mandatory,
|
||||
pbundle->bundle.attr_present,
|
||||
pbundle->method_elm->key_bitmap_len)))
|
||||
bundle->method_elm->key_bitmap_len)))
|
||||
return -EINVAL;
|
||||
|
||||
if (pbundle->method_elm->has_udata)
|
||||
if (bundle->method_elm->has_udata)
|
||||
uverbs_fill_udata(bundle, &pbundle->bundle.driver_udata,
|
||||
UVERBS_ATTR_UHW_IN, UVERBS_ATTR_UHW_OUT);
|
||||
else
|
||||
@@ -499,7 +451,7 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
|
||||
* assume that the driver wrote to its UHW_OUT and flag userspace
|
||||
* appropriately.
|
||||
*/
|
||||
if (!ret && pbundle->method_elm->has_udata) {
|
||||
if (!ret && bundle->method_elm->has_udata) {
|
||||
const struct uverbs_attr *attr =
|
||||
uverbs_attr_get(bundle, UVERBS_ATTR_UHW_OUT);
|
||||
|
||||
@@ -520,7 +472,7 @@ static int ib_uverbs_run_method(struct bundle_priv *pbundle,
|
||||
|
||||
static void bundle_destroy(struct bundle_priv *pbundle, bool commit)
|
||||
{
|
||||
unsigned int key_bitmap_len = pbundle->method_elm->key_bitmap_len;
|
||||
unsigned int key_bitmap_len = pbundle->bundle.method_elm->key_bitmap_len;
|
||||
struct uverbs_attr_bundle *bundle =
|
||||
container_of(&pbundle->bundle, struct uverbs_attr_bundle, hdr);
|
||||
struct bundle_alloc_head *memblock;
|
||||
@@ -608,7 +560,7 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
|
||||
}
|
||||
|
||||
/* Space for the pbundle->bundle.attrs flex array */
|
||||
pbundle->method_elm = method_elm;
|
||||
pbundle->bundle.method_elm = method_elm;
|
||||
pbundle->method_key = attrs_iter.index;
|
||||
pbundle->bundle.ufile = ufile;
|
||||
pbundle->bundle.context = NULL; /* only valid if bundle has uobject */
|
||||
@@ -617,10 +569,12 @@ static int ib_uverbs_cmd_verbs(struct ib_uverbs_file *ufile,
|
||||
pbundle->radix_slots_len = radix_tree_chunk_size(&attrs_iter);
|
||||
pbundle->user_attrs = user_attrs;
|
||||
|
||||
pbundle->internal_used = ALIGN(pbundle->method_elm->key_bitmap_len *
|
||||
sizeof(*container_of(&pbundle->bundle,
|
||||
struct uverbs_attr_bundle, hdr)->attrs),
|
||||
sizeof(*pbundle->internal_buffer));
|
||||
pbundle->internal_used = ALIGN(
|
||||
pbundle->bundle.method_elm->key_bitmap_len *
|
||||
sizeof(*container_of(&pbundle->bundle,
|
||||
struct uverbs_attr_bundle, hdr)
|
||||
->attrs),
|
||||
sizeof(*pbundle->internal_buffer));
|
||||
memset(pbundle->bundle.attr_present, 0,
|
||||
sizeof(pbundle->bundle.attr_present));
|
||||
memset(pbundle->uobj_finalize, 0, sizeof(pbundle->uobj_finalize));
|
||||
@@ -860,77 +814,3 @@ void uverbs_finalize_uobj_create(const struct uverbs_attr_bundle *bundle,
|
||||
pbundle->uobj_hw_obj_valid);
|
||||
}
|
||||
EXPORT_SYMBOL(uverbs_finalize_uobj_create);
|
||||
|
||||
int _ib_copy_validate_udata_in(struct ib_udata *udata, void *req,
|
||||
size_t kernel_size, size_t minimum_size)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (udata->inlen < minimum_size) {
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata too small (%zu < %zu) for ioctl %ps called by %pSR\n",
|
||||
udata->inlen, minimum_size,
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
err = copy_struct_from_user(req, kernel_size, udata->inbuf,
|
||||
udata->inlen);
|
||||
if (err) {
|
||||
if (err == -E2BIG) {
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata not zero from %zu -> %zu for ioctl %ps called by %pSR\n",
|
||||
minimum_size, udata->inlen,
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata EFAULT for ioctl %ps called by %pSR\n",
|
||||
uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_copy_validate_udata_in);
|
||||
|
||||
int _ib_copy_validate_udata_cm_fail(struct ib_udata *udata, u64 req_cm,
|
||||
u64 valid_cm)
|
||||
{
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver input udata has unsupported comp_mask %llx & ~%llx = %llx for ioctl %ps called by %pSR\n",
|
||||
req_cm, valid_cm, req_cm & ~valid_cm,
|
||||
uverbs_get_handler_fn(udata), __builtin_return_address(0));
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_copy_validate_udata_cm_fail);
|
||||
|
||||
int _ib_respond_udata(struct ib_udata *udata, const void *src, size_t len)
|
||||
{
|
||||
size_t copy_len;
|
||||
|
||||
/* 0 length copy_len is a NOP for copy_to_user() and doesn't fail. */
|
||||
copy_len = min(len, udata->outlen);
|
||||
if (copy_to_user(udata->outbuf, src, copy_len))
|
||||
goto err_fault;
|
||||
if (copy_len < udata->outlen) {
|
||||
if (clear_user(udata->outbuf + copy_len,
|
||||
udata->outlen - copy_len))
|
||||
goto err_fault;
|
||||
}
|
||||
return 0;
|
||||
err_fault:
|
||||
ibdev_dbg(
|
||||
rdma_udata_to_dev(udata),
|
||||
"System call driver out udata has EFAULT (%zu into %zu) for ioctl %ps called by %pSR\n",
|
||||
len, udata->outlen, uverbs_get_handler_fn(udata),
|
||||
__builtin_return_address(0));
|
||||
return -EFAULT;
|
||||
}
|
||||
EXPORT_SYMBOL(_ib_respond_udata);
|
||||
@@ -4638,7 +4638,7 @@ int bnxt_re_alloc_ucontext(struct ib_ucontext *ctx, struct ib_udata *udata)
|
||||
|
||||
uctx->rdev = rdev;
|
||||
|
||||
uctx->shpg = (void *)__get_free_page(GFP_KERNEL);
|
||||
uctx->shpg = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!uctx->shpg) {
|
||||
rc = -ENOMEM;
|
||||
goto fail;
|
||||
|
||||
@@ -606,6 +606,7 @@ int mana_ib_query_port(struct ib_device *ibdev, u32 port,
|
||||
if (mana_ib_is_rnic(dev)) {
|
||||
props->gid_tbl_len = 16;
|
||||
props->ip_gids = true;
|
||||
props->max_msg_sz = SZ_16M;
|
||||
if (port == 1)
|
||||
props->port_cap_flags = IB_PORT_CM_SUP;
|
||||
}
|
||||
|
||||
@@ -1081,6 +1081,21 @@ static int siw_get_hdr(struct siw_rx_stream *srx)
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Peer-controlled mpa_len must not underflow srx->fpdu_part_rem
|
||||
* in siw_tcp_rx_data(); a negative value flows as a signed copy
|
||||
* length into siw_check_mem() and skb_copy_bits().
|
||||
*/
|
||||
if (unlikely(be16_to_cpu(c_hdr->mpa_len) + MPA_HDR_SIZE <
|
||||
iwarp_pktinfo[opcode].hdr_len)) {
|
||||
pr_warn_ratelimited("siw: short mpa_len %u for opcode %u (hdr_len %u)\n",
|
||||
be16_to_cpu(c_hdr->mpa_len), opcode,
|
||||
iwarp_pktinfo[opcode].hdr_len);
|
||||
siw_init_terminate(rx_qp(srx), TERM_ERROR_LAYER_LLP,
|
||||
LLP_ETYPE_MPA, LLP_ECODE_FPDU_START, 0);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/*
|
||||
* DDP/RDMAP header receive completed. Check if the current
|
||||
* DDP segment starts a new RDMAP message or continues a previously
|
||||
|
||||
@@ -295,8 +295,8 @@ remove_group:
|
||||
put_kobj:
|
||||
kobject_del(&srv_path->kobj);
|
||||
destroy_root:
|
||||
kobject_put(&srv_path->kobj);
|
||||
rtrs_srv_destroy_once_sysfs_root_folders(srv_path);
|
||||
kobject_put(&srv_path->kobj);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
@@ -1246,7 +1246,9 @@ static struct btrfs_inode *find_first_inode_to_shrink(struct btrfs_root *root,
|
||||
write_unlock(&tree->lock);
|
||||
next:
|
||||
from = btrfs_ino(inode) + 1;
|
||||
cond_resched_lock(&root->inodes.xa_lock);
|
||||
xa_unlock(&root->inodes);
|
||||
cond_resched();
|
||||
xa_lock(&root->inodes);
|
||||
}
|
||||
xa_unlock(&root->inodes);
|
||||
|
||||
|
||||
+8
-3
@@ -633,7 +633,7 @@ again:
|
||||
trans->transid);
|
||||
btrfs_set_file_extent_num_bytes(leaf, fi,
|
||||
end - other_start);
|
||||
return 0;
|
||||
goto mark_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -661,7 +661,7 @@ again:
|
||||
other_end - start);
|
||||
btrfs_set_file_extent_offset(leaf, fi,
|
||||
start - orig_offset);
|
||||
return 0;
|
||||
goto mark_dirty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -788,7 +788,12 @@ again:
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
mark_dirty:
|
||||
ret = btrfs_inode_set_file_extent_range(inode, start, end - start);
|
||||
if (ret)
|
||||
btrfs_abort_transaction(trans, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -155,6 +155,7 @@ enum {
|
||||
BTRFS_FS_LOG_RECOVERING,
|
||||
BTRFS_FS_OPEN,
|
||||
BTRFS_FS_QUOTA_ENABLED,
|
||||
BTRFS_FS_SQUOTA_ENABLING,
|
||||
BTRFS_FS_UPDATE_UUID_TREE_GEN,
|
||||
BTRFS_FS_CREATING_FREE_SPACE_TREE,
|
||||
BTRFS_FS_BTREE_ERR,
|
||||
|
||||
+3
-1
@@ -10699,7 +10699,9 @@ struct btrfs_inode *btrfs_find_first_inode(struct btrfs_root *root, u64 min_ino)
|
||||
break;
|
||||
|
||||
from = btrfs_ino(inode) + 1;
|
||||
cond_resched_lock(&root->inodes.xa_lock);
|
||||
xa_unlock(&root->inodes);
|
||||
cond_resched();
|
||||
xa_lock(&root->inodes);
|
||||
}
|
||||
xa_unlock(&root->inodes);
|
||||
|
||||
|
||||
+68
-33
@@ -1107,7 +1107,13 @@ int btrfs_quota_enable(struct btrfs_fs_info *fs_info,
|
||||
if (simple) {
|
||||
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_SIMPLE_MODE;
|
||||
btrfs_set_fs_incompat(fs_info, SIMPLE_QUOTA);
|
||||
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid);
|
||||
/*
|
||||
* Set the enable generation to the next transaction, as we cannot
|
||||
* ensure that extents written during this transaction will see any
|
||||
* state we have set here. So we should treat all extents of the
|
||||
* transaction as coming in before squotas was enabled.
|
||||
*/
|
||||
btrfs_set_qgroup_status_enable_gen(leaf, ptr, trans->transid + 1);
|
||||
} else {
|
||||
fs_info->qgroup_flags |= BTRFS_QGROUP_STATUS_FLAG_INCONSISTENT;
|
||||
}
|
||||
@@ -1210,7 +1216,15 @@ out_add_root:
|
||||
goto out_free_path;
|
||||
}
|
||||
|
||||
fs_info->qgroup_enable_gen = trans->transid;
|
||||
/*
|
||||
* Set fs_info->qgroup_enable_gen and BTRFS_FS_SQUOTA_ENABLING
|
||||
* under the transaction handle. We want to ensure that all extents in
|
||||
* the next transaction definitely see them.
|
||||
*/
|
||||
if (simple) {
|
||||
fs_info->qgroup_enable_gen = trans->transid + 1;
|
||||
set_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||
}
|
||||
|
||||
mutex_unlock(&fs_info->qgroup_ioctl_lock);
|
||||
/*
|
||||
@@ -1224,9 +1238,15 @@ out_add_root:
|
||||
*/
|
||||
ret = btrfs_commit_transaction(trans);
|
||||
trans = NULL;
|
||||
|
||||
mutex_lock(&fs_info->qgroup_ioctl_lock);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
if (simple) {
|
||||
clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||
fs_info->qgroup_enable_gen = 0;
|
||||
}
|
||||
goto out_free_path;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set quota enabled flag after committing the transaction, to avoid
|
||||
@@ -1236,6 +1256,8 @@ out_add_root:
|
||||
spin_lock(&fs_info->qgroup_lock);
|
||||
fs_info->quota_root = quota_root;
|
||||
set_bit(BTRFS_FS_QUOTA_ENABLED, &fs_info->flags);
|
||||
if (simple)
|
||||
clear_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags);
|
||||
spin_unlock(&fs_info->qgroup_lock);
|
||||
|
||||
/* Skip rescan for simple qgroups. */
|
||||
@@ -1715,32 +1737,24 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool can_delete_parent_qgroup(struct btrfs_qgroup *qgroup)
|
||||
|
||||
static bool can_delete_parent_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup)
|
||||
{
|
||||
ASSERT(btrfs_qgroup_level(qgroup->qgroupid));
|
||||
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
|
||||
squota_check_parent_usage(fs_info, qgroup);
|
||||
return list_empty(&qgroup->members);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if we can delete the squota qgroup and false otherwise.
|
||||
*
|
||||
* Rules for whether we can delete:
|
||||
*
|
||||
* A subvolume qgroup can be removed iff the subvolume is fully deleted, which
|
||||
* is iff there is 0 usage in the qgroup.
|
||||
*
|
||||
* A higher level qgroup can be removed iff it has no members.
|
||||
* Note: We audit its usage to warn on inconsitencies without blocking deletion.
|
||||
* Because a shared extent can outlive its owning subvolume, we cannot delete a
|
||||
* subvol squota qgroup until all of the extents it owns are gone, even if the
|
||||
* subvolume itself has been deleted.
|
||||
*/
|
||||
static bool can_delete_squota_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup *qgroup)
|
||||
static bool can_delete_squota_subvol_qgroup(struct btrfs_fs_info *fs_info,
|
||||
struct btrfs_qgroup *qgroup)
|
||||
{
|
||||
ASSERT(btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE);
|
||||
|
||||
if (btrfs_qgroup_level(qgroup->qgroupid) > 0) {
|
||||
squota_check_parent_usage(fs_info, qgroup);
|
||||
return can_delete_parent_qgroup(qgroup);
|
||||
}
|
||||
ASSERT(btrfs_qgroup_level(qgroup->qgroupid) == 0);
|
||||
|
||||
return !(qgroup->rfer || qgroup->excl || qgroup->rfer_cmpr || qgroup->excl_cmpr);
|
||||
}
|
||||
@@ -1754,14 +1768,11 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
|
||||
{
|
||||
struct btrfs_key key;
|
||||
BTRFS_PATH_AUTO_FREE(path);
|
||||
|
||||
/* Since squotas cannot be inconsistent, they have special rules for deletion. */
|
||||
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
|
||||
return can_delete_squota_qgroup(fs_info, qgroup);
|
||||
int ret;
|
||||
|
||||
/* For higher level qgroup, we can only delete it if it has no child. */
|
||||
if (btrfs_qgroup_level(qgroup->qgroupid))
|
||||
return can_delete_parent_qgroup(qgroup);
|
||||
return can_delete_parent_qgroup(fs_info, qgroup);
|
||||
|
||||
/*
|
||||
* For level-0 qgroups, we can only delete it if it has no subvolume
|
||||
@@ -1777,10 +1788,21 @@ static int can_delete_qgroup(struct btrfs_fs_info *fs_info, struct btrfs_qgroup
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* The @ret from btrfs_find_root() exactly matches our definition for
|
||||
* the return value, thus can be returned directly.
|
||||
* Any subvol qgroup, regardless of mode, cannot be deleted if the
|
||||
* subvol still exists.
|
||||
*/
|
||||
return btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
|
||||
ret = btrfs_find_root(fs_info->tree_root, &key, path, NULL, NULL);
|
||||
/*
|
||||
* btrfs_find_root returns <0 on error, 0 if found, and >0 if not,
|
||||
* so the "found" and "error" cases match our desired return values.
|
||||
*/
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
|
||||
/* Squotas require additional checks, even if the subvol is deleted. */
|
||||
if (btrfs_qgroup_mode(fs_info) == BTRFS_QGROUP_MODE_SIMPLE)
|
||||
return can_delete_squota_subvol_qgroup(fs_info, qgroup);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int btrfs_remove_qgroup(struct btrfs_trans_handle *trans, u64 qgroupid)
|
||||
@@ -4922,7 +4944,8 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||
u64 num_bytes = delta->num_bytes;
|
||||
const int sign = (delta->is_inc ? 1 : -1);
|
||||
|
||||
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE)
|
||||
if (btrfs_qgroup_mode(fs_info) != BTRFS_QGROUP_MODE_SIMPLE &&
|
||||
!test_bit(BTRFS_FS_SQUOTA_ENABLING, &fs_info->flags))
|
||||
return 0;
|
||||
|
||||
if (!btrfs_is_fstree(root))
|
||||
@@ -4934,8 +4957,9 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||
|
||||
spin_lock(&fs_info->qgroup_lock);
|
||||
qgroup = find_qgroup_rb(fs_info, root);
|
||||
if (!qgroup) {
|
||||
ret = -ENOENT;
|
||||
if (WARN_ON_ONCE(!qgroup)) {
|
||||
btrfs_warn(fs_info, "squota failed to find qgroup for root %llu", root);
|
||||
ret = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -4944,8 +4968,19 @@ int btrfs_record_squota_delta(struct btrfs_fs_info *fs_info,
|
||||
list_for_each_entry(qg, &qgroup_list, iterator) {
|
||||
struct btrfs_qgroup_list *glist;
|
||||
|
||||
qg->excl += num_bytes * sign;
|
||||
qg->rfer += num_bytes * sign;
|
||||
ASSERT(qg->excl == qg->rfer);
|
||||
if (WARN_ON_ONCE(sign < 0 && qg->excl < num_bytes)) {
|
||||
btrfs_warn(fs_info,
|
||||
"squota underflow qg %hu/%llu excl %llu num_bytes %llu",
|
||||
btrfs_qgroup_level(qg->qgroupid),
|
||||
btrfs_qgroup_subvolid(qg->qgroupid),
|
||||
qg->excl, num_bytes);
|
||||
qg->excl = 0;
|
||||
qg->rfer = 0;
|
||||
} else {
|
||||
qg->excl += num_bytes * sign;
|
||||
qg->rfer += num_bytes * sign;
|
||||
}
|
||||
qgroup_dirty(fs_info, qg);
|
||||
|
||||
list_for_each_entry(glist, &qg->groups, next_group)
|
||||
|
||||
+13
-2
@@ -3804,8 +3804,19 @@ err_out2:
|
||||
ksmbd_debug(SMB, "Error response: %x\n", rsp->hdr.Status);
|
||||
}
|
||||
|
||||
if (dh_info.reconnected)
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
if (dh_info.reconnected) {
|
||||
/*
|
||||
* If reconnect succeeded, fp was republished in the
|
||||
* session file table. On a later error, ksmbd_fd_put()
|
||||
* above drops the session reference; drop the durable
|
||||
* lookup reference through the same session-aware path so
|
||||
* final close removes the volatile id before freeing fp.
|
||||
*/
|
||||
if (rc && fp == dh_info.fp)
|
||||
ksmbd_fd_put(work, dh_info.fp);
|
||||
else
|
||||
ksmbd_put_durable_fd(dh_info.fp);
|
||||
}
|
||||
|
||||
kfree(name);
|
||||
kfree(lc);
|
||||
|
||||
+50
-16
@@ -1096,6 +1096,40 @@ static int smb_append_inherited_ace(struct smb_ace **ace, int *nt_size,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int smb_validate_ntsd_sid(struct smb_ntsd *pntsd, size_t pntsd_size,
|
||||
unsigned int sid_offset, struct smb_sid **sid,
|
||||
size_t *sid_size)
|
||||
{
|
||||
size_t sid_end;
|
||||
|
||||
*sid = NULL;
|
||||
*sid_size = 0;
|
||||
|
||||
if (!sid_offset)
|
||||
return 0;
|
||||
|
||||
if (sid_offset < sizeof(struct smb_ntsd) ||
|
||||
check_add_overflow(sid_offset, (size_t)CIFS_SID_BASE_SIZE,
|
||||
&sid_end) ||
|
||||
sid_end > pntsd_size)
|
||||
return -EINVAL;
|
||||
|
||||
*sid = (struct smb_sid *)((char *)pntsd + sid_offset);
|
||||
if ((*sid)->num_subauth > SID_MAX_SUB_AUTHORITIES)
|
||||
return -EINVAL;
|
||||
|
||||
if (check_add_overflow((size_t)CIFS_SID_BASE_SIZE,
|
||||
sizeof(__le32) * (size_t)(*sid)->num_subauth,
|
||||
&sid_end))
|
||||
return -EINVAL;
|
||||
|
||||
if (sid_offset > pntsd_size || sid_end > pntsd_size - sid_offset)
|
||||
return -EINVAL;
|
||||
|
||||
*sid_size = sid_end;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int smb_inherit_dacl(struct ksmbd_conn *conn,
|
||||
const struct path *path,
|
||||
unsigned int uid, unsigned int gid)
|
||||
@@ -1108,28 +1142,28 @@ int smb_inherit_dacl(struct ksmbd_conn *conn,
|
||||
struct dentry *parent = path->dentry->d_parent;
|
||||
struct mnt_idmap *idmap = mnt_idmap(path->mnt);
|
||||
int inherited_flags = 0, flags = 0, i, nt_size = 0, pdacl_size;
|
||||
int rc = 0, pntsd_type, pntsd_size, acl_len, aces_size;
|
||||
int rc = 0, pntsd_type, ppntsd_size, acl_len, aces_size;
|
||||
unsigned int dacloffset;
|
||||
size_t dacl_struct_end;
|
||||
u16 num_aces, ace_cnt = 0;
|
||||
char *aces_base;
|
||||
bool is_dir = S_ISDIR(d_inode(path->dentry)->i_mode);
|
||||
|
||||
pntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
|
||||
ppntsd_size = ksmbd_vfs_get_sd_xattr(conn, idmap,
|
||||
parent, &parent_pntsd);
|
||||
if (pntsd_size <= 0)
|
||||
if (ppntsd_size <= 0)
|
||||
return -ENOENT;
|
||||
|
||||
dacloffset = le32_to_cpu(parent_pntsd->dacloffset);
|
||||
if (!dacloffset ||
|
||||
check_add_overflow(dacloffset, sizeof(struct smb_acl), &dacl_struct_end) ||
|
||||
dacl_struct_end > (size_t)pntsd_size) {
|
||||
dacl_struct_end > (size_t)ppntsd_size) {
|
||||
rc = -EINVAL;
|
||||
goto free_parent_pntsd;
|
||||
}
|
||||
|
||||
parent_pdacl = (struct smb_acl *)((char *)parent_pntsd + dacloffset);
|
||||
acl_len = pntsd_size - dacloffset;
|
||||
acl_len = ppntsd_size - dacloffset;
|
||||
num_aces = le16_to_cpu(parent_pdacl->num_aces);
|
||||
pntsd_type = le16_to_cpu(parent_pntsd->type);
|
||||
pdacl_size = le16_to_cpu(parent_pdacl->size);
|
||||
@@ -1243,19 +1277,19 @@ pass:
|
||||
struct smb_ntsd *pntsd;
|
||||
struct smb_acl *pdacl;
|
||||
struct smb_sid *powner_sid = NULL, *pgroup_sid = NULL;
|
||||
int powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size;
|
||||
size_t powner_sid_size = 0, pgroup_sid_size = 0, pntsd_size;
|
||||
size_t pntsd_alloc_size;
|
||||
|
||||
if (parent_pntsd->osidoffset) {
|
||||
powner_sid = (struct smb_sid *)((char *)parent_pntsd +
|
||||
le32_to_cpu(parent_pntsd->osidoffset));
|
||||
powner_sid_size = 1 + 1 + 6 + (powner_sid->num_subauth * 4);
|
||||
}
|
||||
if (parent_pntsd->gsidoffset) {
|
||||
pgroup_sid = (struct smb_sid *)((char *)parent_pntsd +
|
||||
le32_to_cpu(parent_pntsd->gsidoffset));
|
||||
pgroup_sid_size = 1 + 1 + 6 + (pgroup_sid->num_subauth * 4);
|
||||
}
|
||||
rc = smb_validate_ntsd_sid(parent_pntsd, ppntsd_size,
|
||||
le32_to_cpu(parent_pntsd->osidoffset),
|
||||
&powner_sid, &powner_sid_size);
|
||||
if (rc)
|
||||
goto free_aces_base;
|
||||
rc = smb_validate_ntsd_sid(parent_pntsd, ppntsd_size,
|
||||
le32_to_cpu(parent_pntsd->gsidoffset),
|
||||
&pgroup_sid, &pgroup_sid_size);
|
||||
if (rc)
|
||||
goto free_aces_base;
|
||||
|
||||
if (check_add_overflow(sizeof(struct smb_ntsd),
|
||||
(size_t)powner_sid_size,
|
||||
|
||||
@@ -211,7 +211,7 @@ int ksmbd_query_inode_status(struct dentry *dentry)
|
||||
return ret;
|
||||
|
||||
down_read(&ci->m_lock);
|
||||
if (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS))
|
||||
if (ci->m_flags & S_DEL_PENDING)
|
||||
ret = KSMBD_INODE_STATUS_PENDING_DELETE;
|
||||
else
|
||||
ret = KSMBD_INODE_STATUS_OK;
|
||||
@@ -227,7 +227,7 @@ bool ksmbd_inode_pending_delete(struct ksmbd_file *fp)
|
||||
int ret;
|
||||
|
||||
down_read(&ci->m_lock);
|
||||
ret = (ci->m_flags & (S_DEL_PENDING | S_DEL_ON_CLS));
|
||||
ret = (ci->m_flags & S_DEL_PENDING);
|
||||
up_read(&ci->m_lock);
|
||||
|
||||
return ret;
|
||||
@@ -395,12 +395,20 @@ static void __ksmbd_inode_close(struct ksmbd_file *fp)
|
||||
}
|
||||
}
|
||||
|
||||
down_write(&ci->m_lock);
|
||||
/* Promote S_DEL_ON_CLS to S_DEL_PENDING when close */
|
||||
if (ci->m_flags & S_DEL_ON_CLS) {
|
||||
ci->m_flags &= ~S_DEL_ON_CLS;
|
||||
ci->m_flags |= S_DEL_PENDING;
|
||||
}
|
||||
up_write(&ci->m_lock);
|
||||
|
||||
if (atomic_dec_and_test(&ci->m_count)) {
|
||||
bool do_unlink = false;
|
||||
|
||||
down_write(&ci->m_lock);
|
||||
if (ci->m_flags & (S_DEL_ON_CLS | S_DEL_PENDING)) {
|
||||
ci->m_flags &= ~(S_DEL_ON_CLS | S_DEL_PENDING);
|
||||
if (ci->m_flags & S_DEL_PENDING) {
|
||||
ci->m_flags &= ~S_DEL_PENDING;
|
||||
do_unlink = true;
|
||||
}
|
||||
up_write(&ci->m_lock);
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ static int internal_create_group(struct kobject *kobj, int update,
|
||||
kernfs_get(kn);
|
||||
error = create_files(kn, kobj, uid, gid, grp, update);
|
||||
if (error) {
|
||||
if (grp->name)
|
||||
if (grp->name && !update)
|
||||
kernfs_remove(kn);
|
||||
}
|
||||
kernfs_put(kn);
|
||||
|
||||
+24
-10
@@ -472,6 +472,7 @@ xfs_buf_find_insert(
|
||||
/* The new buffer keeps the perag reference until it is freed. */
|
||||
new_bp->b_pag = pag;
|
||||
|
||||
retry:
|
||||
rcu_read_lock();
|
||||
bp = rhashtable_lookup_get_insert_fast(&btp->bt_hash,
|
||||
&new_bp->b_rhash_head, xfs_buf_hash_params);
|
||||
@@ -480,8 +481,16 @@ xfs_buf_find_insert(
|
||||
error = PTR_ERR(bp);
|
||||
goto out_free_buf;
|
||||
}
|
||||
if (bp && lockref_get_not_dead(&bp->b_lockref)) {
|
||||
/* found an existing buffer */
|
||||
if (bp) {
|
||||
/*
|
||||
* If there is an existing buffer with a dead lockref, retry
|
||||
* until the new buffer is added, or a usable buffer is found.
|
||||
*/
|
||||
if (!lockref_get_not_dead(&bp->b_lockref)) {
|
||||
rcu_read_unlock();
|
||||
cpu_relax();
|
||||
goto retry;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
error = xfs_buf_find_lock(bp, flags);
|
||||
if (error)
|
||||
@@ -820,15 +829,20 @@ xfs_buf_destroy(
|
||||
ASSERT(__lockref_is_dead(&bp->b_lockref));
|
||||
ASSERT(!(bp->b_flags & _XBF_DELWRI_Q));
|
||||
|
||||
if (bp->b_pag)
|
||||
xfs_perag_put(bp->b_pag);
|
||||
xfs_buf_free(bp);
|
||||
}
|
||||
|
||||
static inline void
|
||||
xfs_buf_kill(
|
||||
struct xfs_buf *bp)
|
||||
{
|
||||
lockref_mark_dead(&bp->b_lockref);
|
||||
if (!xfs_buf_is_uncached(bp)) {
|
||||
rhashtable_remove_fast(&bp->b_target->bt_hash,
|
||||
&bp->b_rhash_head, xfs_buf_hash_params);
|
||||
|
||||
if (bp->b_pag)
|
||||
xfs_perag_put(bp->b_pag);
|
||||
}
|
||||
|
||||
xfs_buf_free(bp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -851,7 +865,7 @@ xfs_buf_rele(
|
||||
return;
|
||||
|
||||
kill:
|
||||
lockref_mark_dead(&bp->b_lockref);
|
||||
xfs_buf_kill(bp);
|
||||
list_lru_del_obj(&bp->b_target->bt_lru, &bp->b_lru);
|
||||
spin_unlock(&bp->b_lockref.lock);
|
||||
|
||||
@@ -1433,7 +1447,7 @@ xfs_buftarg_drain_rele(
|
||||
return LRU_SKIP;
|
||||
}
|
||||
|
||||
lockref_mark_dead(&bp->b_lockref);
|
||||
xfs_buf_kill(bp);
|
||||
list_lru_isolate_move(lru, item, dispose);
|
||||
spin_unlock(&bp->b_lockref.lock);
|
||||
return LRU_REMOVED;
|
||||
@@ -1545,7 +1559,7 @@ xfs_buftarg_isolate(
|
||||
return LRU_ROTATE;
|
||||
}
|
||||
|
||||
lockref_mark_dead(&bp->b_lockref);
|
||||
xfs_buf_kill(bp);
|
||||
list_lru_isolate_move(lru, item, dispose);
|
||||
spin_unlock(&bp->b_lockref.lock);
|
||||
return LRU_REMOVED;
|
||||
|
||||
+7
-1
@@ -2917,7 +2917,13 @@ int bpf_check_uarg_tail_zero(bpfptr_t uaddr, size_t expected_size,
|
||||
int bpf_check(struct bpf_prog **fp, union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size);
|
||||
|
||||
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
|
||||
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
|
||||
int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth);
|
||||
s32 bpf_call_args_imm(s16 idx);
|
||||
#else
|
||||
static inline s32 bpf_call_args_imm(s16 idx)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct btf *bpf_get_btf_vmlinux(void);
|
||||
|
||||
@@ -729,6 +729,7 @@ struct bpf_subprog_info {
|
||||
*/
|
||||
s16 fastcall_stack_off;
|
||||
bool has_tail_call: 1;
|
||||
bool might_throw: 1;
|
||||
bool tail_call_reachable: 1;
|
||||
bool has_ld_abs: 1;
|
||||
bool is_cb: 1;
|
||||
@@ -1308,6 +1309,7 @@ void bpf_fmt_stack_mask(char *buf, ssize_t buf_sz, u64 stack_mask);
|
||||
bool bpf_subprog_is_global(const struct bpf_verifier_env *env, int subprog);
|
||||
|
||||
int bpf_find_subprog(struct bpf_verifier_env *env, int off);
|
||||
bool bpf_is_throw_kfunc(struct bpf_insn *insn);
|
||||
int bpf_compute_const_regs(struct bpf_verifier_env *env);
|
||||
int bpf_prune_dead_branches(struct bpf_verifier_env *env);
|
||||
int bpf_check_cfg(struct bpf_verifier_env *env);
|
||||
|
||||
@@ -1151,9 +1151,6 @@ bool sk_filter_charge(struct sock *sk, struct sk_filter *fp);
|
||||
void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp);
|
||||
|
||||
u64 __bpf_call_base(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5);
|
||||
#define __bpf_call_base_args \
|
||||
((u64 (*)(u64, u64, u64, u64, u64, const struct bpf_insn *)) \
|
||||
(void *)__bpf_call_base)
|
||||
|
||||
struct bpf_prog *bpf_int_jit_compile(struct bpf_verifier_env *env, struct bpf_prog *prog);
|
||||
void bpf_jit_compile(struct bpf_prog *prog);
|
||||
|
||||
@@ -208,6 +208,7 @@ struct fwnode_operations {
|
||||
static inline void fwnode_init(struct fwnode_handle *fwnode,
|
||||
const struct fwnode_operations *ops)
|
||||
{
|
||||
fwnode->secondary = NULL;
|
||||
fwnode->ops = ops;
|
||||
INIT_LIST_HEAD(&fwnode->consumers);
|
||||
INIT_LIST_HEAD(&fwnode->suppliers);
|
||||
|
||||
@@ -635,6 +635,7 @@ struct uverbs_attr_bundle {
|
||||
struct ib_uverbs_file *ufile;
|
||||
struct ib_ucontext *context;
|
||||
struct ib_uobject *uobject;
|
||||
const struct uverbs_api_ioctl_method *method_elm;
|
||||
DECLARE_BITMAP(attr_present, UVERBS_API_ATTR_BKEY_LEN);
|
||||
);
|
||||
struct uverbs_attr attrs[];
|
||||
|
||||
@@ -827,7 +827,7 @@ const struct bpf_map_ops array_map_ops = {
|
||||
};
|
||||
|
||||
const struct bpf_map_ops percpu_array_map_ops = {
|
||||
.map_meta_equal = bpf_map_meta_equal,
|
||||
.map_meta_equal = array_map_meta_equal,
|
||||
.map_alloc_check = array_map_alloc_check,
|
||||
.map_alloc = array_map_alloc,
|
||||
.map_free = array_map_free,
|
||||
|
||||
+12
-1
@@ -64,11 +64,19 @@ static void mark_subprog_might_sleep(struct bpf_verifier_env *env, int off)
|
||||
subprog->might_sleep = true;
|
||||
}
|
||||
|
||||
static void mark_subprog_might_throw(struct bpf_verifier_env *env, int off)
|
||||
{
|
||||
struct bpf_subprog_info *subprog;
|
||||
|
||||
subprog = bpf_find_containing_subprog(env, off);
|
||||
subprog->might_throw = true;
|
||||
}
|
||||
|
||||
/* 't' is an index of a call-site.
|
||||
* 'w' is a callee entry point.
|
||||
* Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED.
|
||||
* Rely on DFS traversal order and absence of recursive calls to guarantee that
|
||||
* callee's change_pkt_data marks would be correct at that moment.
|
||||
* callee's effect marks would be correct at that moment.
|
||||
*/
|
||||
static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
|
||||
{
|
||||
@@ -78,6 +86,7 @@ static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w)
|
||||
callee = bpf_find_containing_subprog(env, w);
|
||||
caller->changes_pkt_data |= callee->changes_pkt_data;
|
||||
caller->might_sleep |= callee->might_sleep;
|
||||
caller->might_throw |= callee->might_throw;
|
||||
}
|
||||
|
||||
enum {
|
||||
@@ -509,6 +518,8 @@ static int visit_insn(int t, struct bpf_verifier_env *env)
|
||||
mark_subprog_might_sleep(env, t);
|
||||
if (ret == 0 && bpf_is_kfunc_pkt_changing(&meta))
|
||||
mark_subprog_changes_pkt_data(env, t);
|
||||
if (ret == 0 && bpf_is_throw_kfunc(insn))
|
||||
mark_subprog_might_throw(env, t);
|
||||
}
|
||||
return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL);
|
||||
|
||||
|
||||
+19
-8
@@ -1771,6 +1771,9 @@ static u32 abs_s32(s32 x)
|
||||
return x >= 0 ? (u32)x : -(u32)x;
|
||||
}
|
||||
|
||||
static u64 (*interpreters_args[])(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5,
|
||||
const struct bpf_insn *insn);
|
||||
|
||||
/**
|
||||
* ___bpf_prog_run - run eBPF program on a given context
|
||||
* @regs: is the array of MAX_BPF_EXT_REG eBPF pseudo-registers
|
||||
@@ -2077,10 +2080,9 @@ select_insn:
|
||||
CONT;
|
||||
|
||||
JMP_CALL_ARGS:
|
||||
BPF_R0 = (__bpf_call_base_args + insn->imm)(BPF_R1, BPF_R2,
|
||||
BPF_R3, BPF_R4,
|
||||
BPF_R5,
|
||||
insn + insn->off + 1);
|
||||
BPF_R0 = interpreters_args[insn->off](BPF_R1, BPF_R2, BPF_R3,
|
||||
BPF_R4, BPF_R5,
|
||||
insn + insn->imm + 1);
|
||||
CONT;
|
||||
|
||||
JMP_TAIL_CALL: {
|
||||
@@ -2394,13 +2396,22 @@ EVAL4(PROG_NAME_LIST, 416, 448, 480, 512)
|
||||
#undef PROG_NAME_LIST
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
void bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
|
||||
int bpf_patch_call_args(struct bpf_insn *insn, u32 stack_depth)
|
||||
{
|
||||
stack_depth = max_t(u32, stack_depth, 1);
|
||||
insn->off = (s16) insn->imm;
|
||||
insn->imm = interpreters_args[(round_up(stack_depth, 32) / 32) - 1] -
|
||||
__bpf_call_base_args;
|
||||
/* Prevent out-of-bounds read to interpreters_args */
|
||||
if (stack_depth > MAX_BPF_STACK)
|
||||
return -EINVAL;
|
||||
insn->off = (round_up(stack_depth, 32) / 32) - 1;
|
||||
insn->code = BPF_JMP | BPF_CALL_ARGS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
s32 bpf_call_args_imm(s16 idx)
|
||||
{
|
||||
if (WARN_ON_ONCE(idx < 0 || idx >= ARRAY_SIZE(interpreters_args)))
|
||||
return 0;
|
||||
return BPF_CALL_IMM(interpreters_args[idx]);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
+9
-4
@@ -1250,9 +1250,9 @@ static int jit_subprogs(struct bpf_verifier_env *env)
|
||||
}
|
||||
if (!bpf_pseudo_call(insn))
|
||||
continue;
|
||||
insn->off = env->insn_aux_data[i].call_imm;
|
||||
subprog = bpf_find_subprog(env, i + insn->off + 1);
|
||||
insn->imm = subprog;
|
||||
insn->imm = env->insn_aux_data[i].call_imm;
|
||||
subprog = bpf_find_subprog(env, i + insn->imm + 1);
|
||||
insn->off = subprog;
|
||||
}
|
||||
|
||||
prog->jited = 1;
|
||||
@@ -1416,7 +1416,12 @@ int bpf_fixup_call_args(struct bpf_verifier_env *env)
|
||||
depth = get_callee_stack_depth(env, insn, i);
|
||||
if (depth < 0)
|
||||
return depth;
|
||||
bpf_patch_call_args(insn, depth);
|
||||
err = bpf_patch_call_args(insn, depth);
|
||||
if (err) {
|
||||
verbose(env, "stack depth %d exceeds interpreter stack depth limit\n",
|
||||
depth);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
err = 0;
|
||||
#endif
|
||||
|
||||
@@ -4241,8 +4241,13 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p,
|
||||
|
||||
data_len = __bpf_dynptr_size(data_ptr);
|
||||
data = __bpf_dynptr_data(data_ptr, data_len);
|
||||
if (!data)
|
||||
return -EINVAL;
|
||||
|
||||
sig_len = __bpf_dynptr_size(sig_ptr);
|
||||
sig = __bpf_dynptr_data(sig_ptr, sig_len);
|
||||
if (!sig)
|
||||
return -EINVAL;
|
||||
|
||||
return verify_pkcs7_signature(data, data_len, sig, sig_len,
|
||||
trusted_keyring->key,
|
||||
|
||||
@@ -4921,6 +4921,29 @@ out:
|
||||
return map;
|
||||
}
|
||||
|
||||
static void prepare_dump_pseudo_call(struct bpf_insn *insn)
|
||||
{
|
||||
s32 call_off = insn->imm;
|
||||
|
||||
/*
|
||||
* BPF_CALL_ARGS only exists for interpreter fallback.
|
||||
* 1. For interpreter (BPF_CALL_ARGS): insn->off is the index of
|
||||
* interpreters_args array, so here using bpf_call_args_imm()
|
||||
* to get the real address offset.
|
||||
* 2. For JIT (BPF_CALL): insn->off is the subprog id.
|
||||
*/
|
||||
if (insn->code == (BPF_JMP | BPF_CALL_ARGS))
|
||||
insn->imm = bpf_call_args_imm(insn->off);
|
||||
else
|
||||
insn->imm = insn->off;
|
||||
|
||||
/* Avoid dumping a truncated and misleading pc-relative offset. */
|
||||
if (call_off > S16_MAX || call_off < S16_MIN)
|
||||
insn->off = 0;
|
||||
else
|
||||
insn->off = call_off;
|
||||
}
|
||||
|
||||
static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
|
||||
const struct cred *f_cred)
|
||||
{
|
||||
@@ -4946,6 +4969,9 @@ static struct bpf_insn *bpf_insn_prepare_dump(const struct bpf_prog *prog,
|
||||
}
|
||||
if (code == (BPF_JMP | BPF_CALL) ||
|
||||
code == (BPF_JMP | BPF_CALL_ARGS)) {
|
||||
/* Restore the legacy xlated dump layout. */
|
||||
if (insns[i].src_reg == BPF_PSEUDO_CALL)
|
||||
prepare_dump_pseudo_call(&insns[i]);
|
||||
if (code == (BPF_JMP | BPF_CALL_ARGS))
|
||||
insns[i].code = BPF_JMP | BPF_CALL;
|
||||
if (!bpf_dump_raw_ok(f_cred))
|
||||
|
||||
+21
-6
@@ -442,7 +442,6 @@ static bool is_dynptr_ref_function(enum bpf_func_id func_id)
|
||||
static bool is_sync_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_async_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_callback_calling_kfunc(u32 btf_id);
|
||||
static bool is_bpf_throw_kfunc(struct bpf_insn *insn);
|
||||
|
||||
static bool is_bpf_wq_set_callback_kfunc(u32 btf_id);
|
||||
static bool is_task_work_add_kfunc(u32 func_id);
|
||||
@@ -5405,7 +5404,7 @@ continue_func:
|
||||
if (bpf_pseudo_kfunc_call(insn + i) && !insn[i].off) {
|
||||
bool err = false;
|
||||
|
||||
if (!is_bpf_throw_kfunc(insn + i))
|
||||
if (!bpf_is_throw_kfunc(insn + i))
|
||||
continue;
|
||||
for (tmp = idx; tmp >= 0 && !err; tmp = dinfo[tmp].caller) {
|
||||
if (subprog[tmp].is_cb) {
|
||||
@@ -9499,6 +9498,9 @@ static int push_callback_call(struct bpf_verifier_env *env, struct bpf_insn *ins
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
bool *do_print_state, bool exception_exit);
|
||||
|
||||
static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int *insn_idx)
|
||||
{
|
||||
@@ -9552,6 +9554,17 @@ static int check_func_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
caller->regs[BPF_REG_0].subreg_def = DEF_NOT_SUBREG;
|
||||
}
|
||||
|
||||
if (env->subprog_info[subprog].might_throw) {
|
||||
struct bpf_verifier_state *branch;
|
||||
|
||||
branch = push_stack(env, *insn_idx + 1, *insn_idx, false);
|
||||
if (IS_ERR(branch)) {
|
||||
verbose(env, "failed to push state for global subprog exception path\n");
|
||||
return PTR_ERR(branch);
|
||||
}
|
||||
return process_bpf_exit_full(env, NULL, true);
|
||||
}
|
||||
|
||||
/* continue with next insn after call */
|
||||
return 0;
|
||||
}
|
||||
@@ -11263,7 +11276,11 @@ BTF_ID(func, bpf_task_work_schedule_resume)
|
||||
BTF_ID(func, bpf_arena_alloc_pages)
|
||||
BTF_ID(func, bpf_arena_free_pages)
|
||||
BTF_ID(func, bpf_arena_reserve_pages)
|
||||
#ifdef CONFIG_BPF_EVENTS
|
||||
BTF_ID(func, bpf_session_is_return)
|
||||
#else
|
||||
BTF_ID_UNUSED
|
||||
#endif
|
||||
BTF_ID(func, bpf_stream_vprintk)
|
||||
BTF_ID(func, bpf_stream_print_stack)
|
||||
|
||||
@@ -11778,7 +11795,7 @@ static bool is_async_callback_calling_kfunc(u32 btf_id)
|
||||
is_task_work_add_kfunc(btf_id);
|
||||
}
|
||||
|
||||
static bool is_bpf_throw_kfunc(struct bpf_insn *insn)
|
||||
bool bpf_is_throw_kfunc(struct bpf_insn *insn)
|
||||
{
|
||||
return bpf_pseudo_kfunc_call(insn) && insn->off == 0 &&
|
||||
insn->imm == special_kfunc_list[KF_bpf_throw];
|
||||
@@ -12968,8 +12985,6 @@ static int check_special_kfunc(struct bpf_verifier_env *env, struct bpf_kfunc_ca
|
||||
}
|
||||
|
||||
static int check_return_code(struct bpf_verifier_env *env, int regno, const char *reg_name);
|
||||
static int process_bpf_exit_full(struct bpf_verifier_env *env,
|
||||
bool *do_print_state, bool exception_exit);
|
||||
|
||||
static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
int *insn_idx_p)
|
||||
@@ -13350,7 +13365,7 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn,
|
||||
if (meta.func_id == special_kfunc_list[KF_bpf_session_cookie])
|
||||
env->prog->call_session_cookie = true;
|
||||
|
||||
if (is_bpf_throw_kfunc(insn))
|
||||
if (bpf_is_throw_kfunc(insn))
|
||||
return process_bpf_exit_full(env, NULL, true);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -592,13 +592,12 @@ static void emit_signature_match(struct bpf_gen *gen)
|
||||
gen->hash_insn_offset[i] = gen->insn_cur - gen->insn_start;
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_3, 0, 0, 0, 0, 0));
|
||||
|
||||
off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 1;
|
||||
off = -(gen->insn_cur - gen->insn_start - gen->cleanup_label) / 8 - 2;
|
||||
if (is_simm16(off)) {
|
||||
emit(gen, BPF_MOV64_IMM(BPF_REG_7, -EINVAL));
|
||||
emit(gen, BPF_JMP_REG(BPF_JNE, BPF_REG_2, BPF_REG_3, off));
|
||||
} else {
|
||||
gen->error = -ERANGE;
|
||||
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, -1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ static struct {
|
||||
const char *prog_name;
|
||||
int expected_runtime_err;
|
||||
} kfunc_dynptr_tests[] = {
|
||||
{"dynptr_data_null", -EBADMSG},
|
||||
{"dynptr_data_null", -EINVAL},
|
||||
};
|
||||
|
||||
static bool kfunc_not_supported;
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <test_progs.h>
|
||||
|
||||
/*
|
||||
* Test that replacing an inner percpu array map with one that has different
|
||||
* max_entries is rejected. percpu_array_map_gen_lookup() inlines the
|
||||
* template's index_mask, so allowing a smaller replacement would cause OOB.
|
||||
*/
|
||||
void test_percpu_array_inner_map(void)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_map_create_opts, opts);
|
||||
int outer_fd, tmpl_fd, good_fd, bad_fd, err;
|
||||
int zero = 0;
|
||||
|
||||
/* Create template: percpu array with 8 entries */
|
||||
tmpl_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "tmpl",
|
||||
sizeof(int), sizeof(long), 8, NULL);
|
||||
if (!ASSERT_OK_FD(tmpl_fd, "create_tmpl"))
|
||||
return;
|
||||
|
||||
/* Create outer array-of-maps using template */
|
||||
opts.inner_map_fd = tmpl_fd;
|
||||
outer_fd = bpf_map_create(BPF_MAP_TYPE_ARRAY_OF_MAPS, "outer",
|
||||
sizeof(int), sizeof(int), 1, &opts);
|
||||
if (!ASSERT_OK_FD(outer_fd, "create_outer"))
|
||||
goto close_tmpl;
|
||||
|
||||
/* Insert template as initial inner map */
|
||||
err = bpf_map_update_elem(outer_fd, &zero, &tmpl_fd, 0);
|
||||
if (!ASSERT_OK(err, "insert_tmpl"))
|
||||
goto close_outer;
|
||||
|
||||
/* Replacement with same max_entries should succeed */
|
||||
good_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "good",
|
||||
sizeof(int), sizeof(long), 8, NULL);
|
||||
if (!ASSERT_OK_FD(good_fd, "create_good"))
|
||||
goto close_outer;
|
||||
|
||||
err = bpf_map_update_elem(outer_fd, &zero, &good_fd, 0);
|
||||
ASSERT_OK(err, "replace_same_max_entries");
|
||||
close(good_fd);
|
||||
|
||||
/* Replacement with fewer max_entries must fail */
|
||||
bad_fd = bpf_map_create(BPF_MAP_TYPE_PERCPU_ARRAY, "bad",
|
||||
sizeof(int), sizeof(long), 2, NULL);
|
||||
if (!ASSERT_OK_FD(bad_fd, "create_bad"))
|
||||
goto close_outer;
|
||||
|
||||
err = bpf_map_update_elem(outer_fd, &zero, &bad_fd, 0);
|
||||
ASSERT_ERR(err, "replace_smaller_max_entries");
|
||||
close(bad_fd);
|
||||
|
||||
close_outer:
|
||||
close(outer_fd);
|
||||
close_tmpl:
|
||||
close(tmpl_fd);
|
||||
}
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "verifier_bswap.skel.h"
|
||||
#include "verifier_btf_ctx_access.skel.h"
|
||||
#include "verifier_btf_unreliable_prog.skel.h"
|
||||
#include "verifier_call_large_imm.skel.h"
|
||||
#include "verifier_cfg.skel.h"
|
||||
#include "verifier_cgroup_inv_retcode.skel.h"
|
||||
#include "verifier_cgroup_skb.skel.h"
|
||||
@@ -170,6 +171,7 @@ void test_verifier_bpf_trap(void) { RUN(verifier_bpf_trap); }
|
||||
void test_verifier_bswap(void) { RUN(verifier_bswap); }
|
||||
void test_verifier_btf_ctx_access(void) { RUN(verifier_btf_ctx_access); }
|
||||
void test_verifier_btf_unreliable_prog(void) { RUN(verifier_btf_unreliable_prog); }
|
||||
void test_verifier_call_large_imm(void) { RUN(verifier_call_large_imm); }
|
||||
void test_verifier_cfg(void) { RUN(verifier_cfg); }
|
||||
void test_verifier_cgroup_inv_retcode(void) { RUN(verifier_cgroup_inv_retcode); }
|
||||
void test_verifier_cgroup_skb(void) { RUN(verifier_cgroup_skb); }
|
||||
|
||||
@@ -208,6 +208,28 @@ int reject_with_reference(void *ctx)
|
||||
return 0;
|
||||
}
|
||||
|
||||
__noinline int global_subprog_may_throw(struct __sk_buff *ctx)
|
||||
{
|
||||
if (ctx->len)
|
||||
bpf_throw(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("?tc")
|
||||
__failure __msg("Unreleased reference")
|
||||
int reject_global_subprog_throw_with_reference(struct __sk_buff *ctx)
|
||||
{
|
||||
struct foo *f;
|
||||
|
||||
f = bpf_obj_new(typeof(*f));
|
||||
if (!f)
|
||||
return 0;
|
||||
if (ctx->protocol)
|
||||
global_subprog_may_throw(ctx);
|
||||
bpf_obj_drop(f);
|
||||
return 0;
|
||||
}
|
||||
|
||||
__noinline static int subprog_ref(struct __sk_buff *ctx)
|
||||
{
|
||||
struct foo *f;
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/bpf.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "bpf_misc.h"
|
||||
|
||||
int call_happened = 0;
|
||||
|
||||
/*
|
||||
* 32765 is the exact minimum number of padding instructions needed to
|
||||
* trigger the verifier failure, because:
|
||||
* 1. Counting the wrapper instructions around the padding block (one
|
||||
* "r0=0" and two "exit" instructions), the actual jump distance
|
||||
* evaluates to N + 3.
|
||||
* 2. To overflow the s16 max bound (32767), we need N + 3 > 32767.
|
||||
* Thus, N = 32765 is the exact minimum padding size required.
|
||||
*/
|
||||
static __attribute__((noinline)) void padding_subprog(void)
|
||||
{
|
||||
asm volatile (
|
||||
"r0 = 0;"
|
||||
".rept 32765;"
|
||||
"r0 += 0;"
|
||||
".endr;"
|
||||
::: __clobber_all);
|
||||
}
|
||||
|
||||
static __attribute__((noinline)) int target_subprog(void)
|
||||
{
|
||||
/* Use volatile variable here to prevent optimization. */
|
||||
volatile int magic_ret = 3;
|
||||
return magic_ret;
|
||||
}
|
||||
|
||||
SEC("syscall")
|
||||
__success __retval(3)
|
||||
int call_large_imm_test(void *ctx)
|
||||
{
|
||||
/*
|
||||
* Landing pad to handle call error on kernel without the fix,
|
||||
* preventing kernel panic.
|
||||
*/
|
||||
asm volatile (
|
||||
"r0 = 0;"
|
||||
".rept 32768;"
|
||||
"r0 += 0;"
|
||||
".endr;"
|
||||
::: __clobber_all);
|
||||
|
||||
/*
|
||||
* The call_happened variable is 1 only when the call insn wrongly
|
||||
* go back to the landing pad above.
|
||||
*/
|
||||
if (call_happened == 1) {
|
||||
/* Use volatile variable here to prevent optimization. */
|
||||
volatile int flag = -1;
|
||||
return flag;
|
||||
}
|
||||
|
||||
call_happened = 1;
|
||||
|
||||
padding_subprog();
|
||||
|
||||
return target_subprog();
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
@@ -8,6 +8,8 @@ RXE_NAME="rxe6"
|
||||
PORT=4791
|
||||
IP6_ADDR="2001:db8::1/64"
|
||||
|
||||
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
|
||||
|
||||
exec > /dev/null
|
||||
|
||||
# Cleanup function to run on exit (even on failure)
|
||||
@@ -21,8 +23,8 @@ trap cleanup EXIT
|
||||
# 1. Prerequisites check
|
||||
for mod in tun veth rdma_rxe; do
|
||||
if ! modinfo "$mod" >/dev/null 2>&1; then
|
||||
echo "Error: Kernel module '$mod' not found."
|
||||
exit 1
|
||||
echo "SKIP: Kernel module '$mod' not found." >&2
|
||||
exit $KSFT_SKIP
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ IP_A="1.1.1.1"
|
||||
IP_B="1.1.1.2"
|
||||
PORT=4791
|
||||
|
||||
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
|
||||
|
||||
exec > /dev/null
|
||||
|
||||
# --- Cleanup Routine ---
|
||||
@@ -27,6 +29,11 @@ if [[ $EUID -ne 0 ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! modinfo rdma_rxe >/dev/null 2>&1; then
|
||||
echo "SKIP: Kernel module 'rdma_rxe' not found." >&2
|
||||
exit $KSFT_SKIP
|
||||
fi
|
||||
|
||||
modprobe rdma_rxe || { echo "Failed to load rdma_rxe"; exit 1; }
|
||||
|
||||
# --- Setup Network Topology ---
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
PORT=4791
|
||||
MODS=("tun" "rdma_rxe")
|
||||
|
||||
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
|
||||
|
||||
exec > /dev/null
|
||||
|
||||
# --- Helper: Cleanup Routine ---
|
||||
@@ -26,6 +28,10 @@ if [[ $EUID -ne 0 ]]; then
|
||||
fi
|
||||
|
||||
for m in "${MODS[@]}"; do
|
||||
if ! modinfo "$m" >/dev/null 2>&1; then
|
||||
echo "SKIP: Kernel module '$m' not found." >&2
|
||||
exit $KSFT_SKIP
|
||||
fi
|
||||
modprobe "$m" || { echo "Error: Failed to load $m"; exit 1; }
|
||||
done
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@ DEV_NAME="tun0"
|
||||
RXE_NAME="rxe0"
|
||||
RDMA_PORT=4791
|
||||
|
||||
source "$(dirname "$0")/../kselftest/ktap_helpers.sh"
|
||||
|
||||
exec > /dev/null
|
||||
|
||||
# --- Cleanup Routine ---
|
||||
@@ -19,8 +21,8 @@ trap cleanup EXIT
|
||||
|
||||
# 1. Dependency Check
|
||||
if ! modinfo rdma_rxe >/dev/null 2>&1; then
|
||||
echo "Error: rdma_rxe module not found."
|
||||
exit 1
|
||||
echo "SKIP: rdma_rxe module not found." >&2
|
||||
exit $KSFT_SKIP
|
||||
fi
|
||||
|
||||
modprobe rdma_rxe
|
||||
|
||||
Reference in new issue
Block a user