diff options
author | Paul Mackerras <paulus@ozlabs.org> | 2018-01-19 12:09:57 +1100 |
---|---|---|
committer | Paul Mackerras <paulus@ozlabs.org> | 2018-01-19 12:09:57 +1100 |
commit | d27998185da8fbdc35911307ae13518d168778d7 (patch) | |
tree | 4c5a99fd7cff7c9da1f858fdfccf8dc1cc6c597b /tools/testing | |
parent | 00608e1f007e4cf6031485c5630e0e504bceef9b (diff) | |
parent | d075745d893c78730e4a3b7a60fca23c2f764081 (diff) |
Merge remote-tracking branch 'remotes/powerpc/topic/ppc-kvm' into kvm-ppc-next
This merges in the ppc-kvm topic branch of the powerpc tree to get
two patches which are prerequisites for the following patch series,
plus another patch which touches both powerpc and KVM code.
Signed-off-by: Paul Mackerras <paulus@ozlabs.org>
Diffstat (limited to 'tools/testing')
66 files changed, 2046 insertions, 142 deletions
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index 65368d9027f5..db33b28c5ef3 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild @@ -70,6 +70,7 @@ libnvdimm-y += $(NVDIMM_SRC)/region_devs.o libnvdimm-y += $(NVDIMM_SRC)/region.o libnvdimm-y += $(NVDIMM_SRC)/namespace_devs.o libnvdimm-y += $(NVDIMM_SRC)/label.o +libnvdimm-y += $(NVDIMM_SRC)/badrange.o libnvdimm-$(CONFIG_ND_CLAIM) += $(NVDIMM_SRC)/claim.o libnvdimm-$(CONFIG_BTT) += $(NVDIMM_SRC)/btt_devs.o libnvdimm-$(CONFIG_NVDIMM_PFN) += $(NVDIMM_SRC)/pfn_devs.o diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index bef419d4266d..7217b2b953b5 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c @@ -168,8 +168,12 @@ struct nfit_test { spinlock_t lock; } ars_state; struct device *dimm_dev[NUM_DCR]; + struct badrange badrange; + struct work_struct work; }; +static struct workqueue_struct *nfit_wq; + static struct nfit_test *to_nfit_test(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -234,48 +238,68 @@ static int nfit_test_cmd_set_config_data(struct nd_cmd_set_config_hdr *nd_cmd, return rc; } -#define NFIT_TEST_ARS_RECORDS 4 #define NFIT_TEST_CLEAR_ERR_UNIT 256 static int nfit_test_cmd_ars_cap(struct nd_cmd_ars_cap *nd_cmd, unsigned int buf_len) { + int ars_recs; + if (buf_len < sizeof(*nd_cmd)) return -EINVAL; + /* for testing, only store up to n records that fit within 4k */ + ars_recs = SZ_4K / sizeof(struct nd_ars_record); + nd_cmd->max_ars_out = sizeof(struct nd_cmd_ars_status) - + NFIT_TEST_ARS_RECORDS * sizeof(struct nd_ars_record); + + ars_recs * sizeof(struct nd_ars_record); nd_cmd->status = (ND_ARS_PERSISTENT | ND_ARS_VOLATILE) << 16; nd_cmd->clear_err_unit = NFIT_TEST_CLEAR_ERR_UNIT; return 0; } -/* - * Initialize the ars_state to return an ars_result 1 second in the future with - * a 4K error range in the middle of the requested address range. - */ -static void post_ars_status(struct ars_state *ars_state, u64 addr, u64 len) +static void post_ars_status(struct ars_state *ars_state, + struct badrange *badrange, u64 addr, u64 len) { struct nd_cmd_ars_status *ars_status; struct nd_ars_record *ars_record; + struct badrange_entry *be; + u64 end = addr + len - 1; + int i = 0; ars_state->deadline = jiffies + 1*HZ; ars_status = ars_state->ars_status; ars_status->status = 0; - ars_status->out_length = sizeof(struct nd_cmd_ars_status) - + sizeof(struct nd_ars_record); ars_status->address = addr; ars_status->length = len; ars_status->type = ND_ARS_PERSISTENT; - ars_status->num_records = 1; - ars_record = &ars_status->records[0]; - ars_record->handle = 0; - ars_record->err_address = addr + len / 2; - ars_record->length = SZ_4K; + + spin_lock(&badrange->lock); + list_for_each_entry(be, &badrange->list, list) { + u64 be_end = be->start + be->length - 1; + u64 rstart, rend; + + /* skip entries outside the range */ + if (be_end < addr || be->start > end) + continue; + + rstart = (be->start < addr) ? addr : be->start; + rend = (be_end < end) ? be_end : end; + ars_record = &ars_status->records[i]; + ars_record->handle = 0; + ars_record->err_address = rstart; + ars_record->length = rend - rstart + 1; + i++; + } + spin_unlock(&badrange->lock); + ars_status->num_records = i; + ars_status->out_length = sizeof(struct nd_cmd_ars_status) + + i * sizeof(struct nd_ars_record); } -static int nfit_test_cmd_ars_start(struct ars_state *ars_state, +static int nfit_test_cmd_ars_start(struct nfit_test *t, + struct ars_state *ars_state, struct nd_cmd_ars_start *ars_start, unsigned int buf_len, int *cmd_rc) { @@ -289,7 +313,7 @@ static int nfit_test_cmd_ars_start(struct ars_state *ars_state, } else { ars_start->status = 0; ars_start->scrub_time = 1; - post_ars_status(ars_state, ars_start->address, + post_ars_status(ars_state, &t->badrange, ars_start->address, ars_start->length); *cmd_rc = 0; } @@ -320,7 +344,8 @@ static int nfit_test_cmd_ars_status(struct ars_state *ars_state, return 0; } -static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, +static int nfit_test_cmd_clear_error(struct nfit_test *t, + struct nd_cmd_clear_error *clear_err, unsigned int buf_len, int *cmd_rc) { const u64 mask = NFIT_TEST_CLEAR_ERR_UNIT - 1; @@ -330,18 +355,91 @@ static int nfit_test_cmd_clear_error(struct nd_cmd_clear_error *clear_err, if ((clear_err->address & mask) || (clear_err->length & mask)) return -EINVAL; - /* - * Report 'all clear' success for all commands even though a new - * scrub will find errors again. This is enough to have the - * error removed from the 'badblocks' tracking in the pmem - * driver. - */ + badrange_forget(&t->badrange, clear_err->address, clear_err->length); clear_err->status = 0; clear_err->cleared = clear_err->length; *cmd_rc = 0; return 0; } +struct region_search_spa { + u64 addr; + struct nd_region *region; +}; + +static int is_region_device(struct device *dev) +{ + return !strncmp(dev->kobj.name, "region", 6); +} + +static int nfit_test_search_region_spa(struct device *dev, void *data) +{ + struct region_search_spa *ctx = data; + struct nd_region *nd_region; + resource_size_t ndr_end; + + if (!is_region_device(dev)) + return 0; + + nd_region = to_nd_region(dev); + ndr_end = nd_region->ndr_start + nd_region->ndr_size; + + if (ctx->addr >= nd_region->ndr_start && ctx->addr < ndr_end) { + ctx->region = nd_region; + return 1; + } + + return 0; +} + +static int nfit_test_search_spa(struct nvdimm_bus *bus, + struct nd_cmd_translate_spa *spa) +{ + int ret; + struct nd_region *nd_region = NULL; + struct nvdimm *nvdimm = NULL; + struct nd_mapping *nd_mapping = NULL; + struct region_search_spa ctx = { + .addr = spa->spa, + .region = NULL, + }; + u64 dpa; + + ret = device_for_each_child(&bus->dev, &ctx, + nfit_test_search_region_spa); + + if (!ret) + return -ENODEV; + + nd_region = ctx.region; + + dpa = ctx.addr - nd_region->ndr_start; + + /* + * last dimm is selected for test + */ + nd_mapping = &nd_region->mapping[nd_region->ndr_mappings - 1]; + nvdimm = nd_mapping->nvdimm; + + spa->devices[0].nfit_device_handle = handle[nvdimm->id]; + spa->num_nvdimms = 1; + spa->devices[0].dpa = dpa; + + return 0; +} + +static int nfit_test_cmd_translate_spa(struct nvdimm_bus *bus, + struct nd_cmd_translate_spa *spa, unsigned int buf_len) +{ + if (buf_len < spa->translate_length) + return -EINVAL; + + if (nfit_test_search_spa(bus, spa) < 0 || !spa->num_nvdimms) + spa->status = 2; + + return 0; +} + static int nfit_test_cmd_smart(struct nd_cmd_smart *smart, unsigned int buf_len) { static const struct nd_smart_payload smart_data = { @@ -378,6 +476,93 @@ static int nfit_test_cmd_smart_threshold(struct nd_cmd_smart_threshold *smart_t, return 0; } +static void uc_error_notify(struct work_struct *work) +{ + struct nfit_test *t = container_of(work, typeof(*t), work); + + __acpi_nfit_notify(&t->pdev.dev, t, NFIT_NOTIFY_UC_MEMORY_ERROR); +} + +static int nfit_test_cmd_ars_error_inject(struct nfit_test *t, + struct nd_cmd_ars_err_inj *err_inj, unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*err_inj)) { + rc = -EINVAL; + goto err; + } + + if (err_inj->err_inj_spa_range_length <= 0) { + rc = -EINVAL; + goto err; + } + + rc = badrange_add(&t->badrange, err_inj->err_inj_spa_range_base, + err_inj->err_inj_spa_range_length); + if (rc < 0) + goto err; + + if (err_inj->err_inj_options & (1 << ND_ARS_ERR_INJ_OPT_NOTIFY)) + queue_work(nfit_wq, &t->work); + + err_inj->status = 0; + return 0; + +err: + err_inj->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int nfit_test_cmd_ars_inject_clear(struct nfit_test *t, + struct nd_cmd_ars_err_inj_clr *err_clr, unsigned int buf_len) +{ + int rc; + + if (buf_len != sizeof(*err_clr)) { + rc = -EINVAL; + goto err; + } + + if (err_clr->err_inj_clr_spa_range_length <= 0) { + rc = -EINVAL; + goto err; + } + + badrange_forget(&t->badrange, err_clr->err_inj_clr_spa_range_base, + err_clr->err_inj_clr_spa_range_length); + + err_clr->status = 0; + return 0; + +err: + err_clr->status = NFIT_ARS_INJECT_INVALID; + return rc; +} + +static int nfit_test_cmd_ars_inject_status(struct nfit_test *t, + struct nd_cmd_ars_err_inj_stat *err_stat, + unsigned int buf_len) +{ + struct badrange_entry *be; + int max = SZ_4K / sizeof(struct nd_error_stat_query_record); + int i = 0; + + err_stat->status = 0; + spin_lock(&t->badrange.lock); + list_for_each_entry(be, &t->badrange.list, list) { + err_stat->record[i].err_inj_stat_spa_range_base = be->start; + err_stat->record[i].err_inj_stat_spa_range_length = be->length; + i++; + if (i > max) + break; + } + spin_unlock(&t->badrange.lock); + err_stat->inj_err_rec_count = i; + + return 0; +} + static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, struct nvdimm *nvdimm, unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc) @@ -449,6 +634,38 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, } } else { struct ars_state *ars_state = &t->ars_state; + struct nd_cmd_pkg *call_pkg = buf; + + if (!nd_desc) + return -ENOTTY; + + if (cmd == ND_CMD_CALL) { + func = call_pkg->nd_command; + + buf_len = call_pkg->nd_size_in + call_pkg->nd_size_out; + buf = (void *) call_pkg->nd_payload; + + switch (func) { + case NFIT_CMD_TRANSLATE_SPA: + rc = nfit_test_cmd_translate_spa( + acpi_desc->nvdimm_bus, buf, buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_SET: + rc = nfit_test_cmd_ars_error_inject(t, buf, + buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_CLEAR: + rc = nfit_test_cmd_ars_inject_clear(t, buf, + buf_len); + return rc; + case NFIT_CMD_ARS_INJECT_GET: + rc = nfit_test_cmd_ars_inject_status(t, buf, + buf_len); + return rc; + default: + return -ENOTTY; + } + } if (!nd_desc || !test_bit(cmd, &nd_desc->cmd_mask)) return -ENOTTY; @@ -458,15 +675,15 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, rc = nfit_test_cmd_ars_cap(buf, buf_len); break; case ND_CMD_ARS_START: - rc = nfit_test_cmd_ars_start(ars_state, buf, buf_len, - cmd_rc); + rc = nfit_test_cmd_ars_start(t, ars_state, buf, + buf_len, cmd_rc); break; case ND_CMD_ARS_STATUS: rc = nfit_test_cmd_ars_status(ars_state, buf, buf_len, cmd_rc); break; case ND_CMD_CLEAR_ERROR: - rc = nfit_test_cmd_clear_error(buf, buf_len, cmd_rc); + rc = nfit_test_cmd_clear_error(t, buf, buf_len, cmd_rc); break; default: return -ENOTTY; @@ -566,10 +783,9 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) static int ars_state_init(struct device *dev, struct ars_state *ars_state) { + /* for testing, only store up to n records that fit within 4k */ ars_state->ars_status = devm_kzalloc(dev, - sizeof(struct nd_cmd_ars_status) - + sizeof(struct nd_ars_record) * NFIT_TEST_ARS_RECORDS, - GFP_KERNEL); + sizeof(struct nd_cmd_ars_status) + SZ_4K, GFP_KERNEL); if (!ars_state->ars_status) return -ENOMEM; spin_lock_init(&ars_state->lock); @@ -1419,7 +1635,8 @@ static void nfit_test0_setup(struct nfit_test *t) + i * sizeof(u64); } - post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA0_SIZE); + post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], + SPA0_SIZE); acpi_desc = &t->acpi_desc; set_bit(ND_CMD_GET_CONFIG_SIZE, &acpi_desc->dimm_cmd_force_en); @@ -1430,7 +1647,12 @@ static void nfit_test0_setup(struct nfit_test *t) set_bit(ND_CMD_ARS_START, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_ARS_STATUS, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_CLEAR_ERROR, &acpi_desc->bus_cmd_force_en); + set_bit(ND_CMD_CALL, &acpi_desc->bus_cmd_force_en); set_bit(ND_CMD_SMART_THRESHOLD, &acpi_desc->dimm_cmd_force_en); + set_bit(NFIT_CMD_TRANSLATE_SPA, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_SET, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_CLEAR, &acpi_desc->bus_nfit_cmd_force_en); + set_bit(NFIT_CMD_ARS_INJECT_GET, &acpi_desc->bus_nfit_cmd_force_en); } static void nfit_test1_setup(struct nfit_test *t) @@ -1520,7 +1742,8 @@ static void nfit_test1_setup(struct nfit_test *t) dcr->code = NFIT_FIC_BYTE; dcr->windows = 0; - post_ars_status(&t->ars_state, t->spa_set_dma[0], SPA2_SIZE); + post_ars_status(&t->ars_state, &t->badrange, t->spa_set_dma[0], + SPA2_SIZE); acpi_desc = &t->acpi_desc; set_bit(ND_CMD_ARS_CAP, &acpi_desc->bus_cmd_force_en); @@ -1589,6 +1812,7 @@ static int nfit_ctl_test(struct device *dev) unsigned long mask, cmd_size, offset; union { struct nd_cmd_get_config_size cfg_size; + struct nd_cmd_clear_error clear_err; struct nd_cmd_ars_status ars_stat; struct nd_cmd_ars_cap ars_cap; char buf[sizeof(struct nd_cmd_ars_status) @@ -1613,10 +1837,15 @@ static int nfit_ctl_test(struct device *dev) .cmd_mask = 1UL << ND_CMD_ARS_CAP | 1UL << ND_CMD_ARS_START | 1UL << ND_CMD_ARS_STATUS - | 1UL << ND_CMD_CLEAR_ERROR, + | 1UL << ND_CMD_CLEAR_ERROR + | 1UL << ND_CMD_CALL, .module = THIS_MODULE, .provider_name = "ACPI.NFIT", .ndctl = acpi_nfit_ctl, + .bus_dsm_mask = 1UL << NFIT_CMD_TRANSLATE_SPA + | 1UL << NFIT_CMD_ARS_INJECT_SET + | 1UL << NFIT_CMD_ARS_INJECT_CLEAR + | 1UL << NFIT_CMD_ARS_INJECT_GET, }, .dev = &adev->dev, }; @@ -1767,6 +1996,23 @@ static int nfit_ctl_test(struct device *dev) return -EIO; } + /* test clear error */ + cmd_size = sizeof(cmds.clear_err); + cmds.clear_err = (struct nd_cmd_clear_error) { + .length = 512, + .cleared = 512, + }; + rc = setup_result(cmds.buf, cmd_size); + if (rc) + return rc; + rc = acpi_nfit_ctl(&acpi_desc->nd_desc, NULL, ND_CMD_CLEAR_ERROR, + cmds.buf, cmd_size, &cmd_rc); + if (rc < 0 || cmd_rc) { + dev_dbg(dev, "%s: failed at: %d rc: %d cmd_rc: %d\n", + __func__, __LINE__, rc, cmd_rc); + return -EIO; + } + return 0; } @@ -1915,6 +2161,10 @@ static __init int nfit_test_init(void) nfit_test_setup(nfit_test_lookup, nfit_test_evaluate_dsm); + nfit_wq = create_singlethread_workqueue("nfit"); + if (!nfit_wq) + return -ENOMEM; + nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm"); if (IS_ERR(nfit_test_dimm)) { rc = PTR_ERR(nfit_test_dimm); @@ -1931,6 +2181,7 @@ static __init int nfit_test_init(void) goto err_register; } INIT_LIST_HEAD(&nfit_test->resources); + badrange_init(&nfit_test->badrange); switch (i) { case 0: nfit_test->num_pm = NUM_PM; @@ -1966,6 +2217,7 @@ static __init int nfit_test_init(void) goto err_register; instances[i] = nfit_test; + INIT_WORK(&nfit_test->work, uc_error_notify); } rc = platform_driver_register(&nfit_test_driver); @@ -1974,6 +2226,7 @@ static __init int nfit_test_init(void) return 0; err_register: + destroy_workqueue(nfit_wq); for (i = 0; i < NUM_NFITS; i++) if (instances[i]) platform_device_unregister(&instances[i]->pdev); @@ -1989,6 +2242,8 @@ static __exit void nfit_test_exit(void) { int i; + flush_workqueue(nfit_wq); + destroy_workqueue(nfit_wq); for (i = 0; i < NUM_NFITS; i++) platform_device_unregister(&instances[i]->pdev); platform_driver_unregister(&nfit_test_driver); diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index d3d63dd5ed38..113b44675a71 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h @@ -32,6 +32,58 @@ struct nfit_test_resource { void *buf; }; +#define ND_TRANSLATE_SPA_STATUS_INVALID_SPA 2 +#define NFIT_ARS_INJECT_INVALID 2 + +enum err_inj_options { + ND_ARS_ERR_INJ_OPT_NOTIFY = 0, +}; + +/* nfit commands */ +enum nfit_cmd_num { + NFIT_CMD_TRANSLATE_SPA = 5, + NFIT_CMD_ARS_INJECT_SET = 7, + NFIT_CMD_ARS_INJECT_CLEAR = 8, + NFIT_CMD_ARS_INJECT_GET = 9, +}; + +struct nd_cmd_translate_spa { + __u64 spa; + __u32 status; + __u8 flags; + __u8 _reserved[3]; + __u64 translate_length; + __u32 num_nvdimms; + struct nd_nvdimm_device { + __u32 nfit_device_handle; + __u32 _reserved; + __u64 dpa; + } __packed devices[0]; + +} __packed; + +struct nd_cmd_ars_err_inj { + __u64 err_inj_spa_range_base; + __u64 err_inj_spa_range_length; + __u8 err_inj_options; + __u32 status; +} __packed; + +struct nd_cmd_ars_err_inj_clr { + __u64 err_inj_clr_spa_range_base; + __u64 err_inj_clr_spa_range_length; + __u32 status; +} __packed; + +struct nd_cmd_ars_err_inj_stat { + __u32 status; + __u32 inj_err_rec_count; + struct nd_error_stat_query_record { + __u64 err_inj_stat_spa_range_base; + __u64 err_inj_stat_spa_range_length; + } __packed record[0]; +} __packed; + union acpi_object; typedef void *acpi_handle; diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index 3c9c0bbe7dbb..eaf599dc2137 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 -TARGETS = bpf +TARGETS = android +TARGETS += bpf TARGETS += breakpoints TARGETS += capabilities TARGETS += cpufreq diff --git a/tools/testing/selftests/android/Makefile b/tools/testing/selftests/android/Makefile new file mode 100644 index 000000000000..1a7492268993 --- /dev/null +++ b/tools/testing/selftests/android/Makefile @@ -0,0 +1,46 @@ +SUBDIRS := ion + +TEST_PROGS := run.sh + +.PHONY: all clean + +include ../lib.mk + +all: + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + #SUBDIR test prog name should be in the form: SUBDIR_test.sh + TEST=$$DIR"_test.sh"; \ + if [ -e $$DIR/$$TEST ]; then + rsync -a $$DIR/$$TEST $$BUILD_TARGET/; + fi + done + +override define RUN_TESTS + @cd $(OUTPUT); ./run.sh +endef + +override define INSTALL_RULE + mkdir -p $(INSTALL_PATH) + install -t $(INSTALL_PATH) $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) + + @for SUBDIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$SUBDIR; \ + mkdir $$BUILD_TARGET -p; \ + $(MAKE) OUTPUT=$$BUILD_TARGET -C $$SUBDIR INSTALL_PATH=$(INSTALL_PATH)/$$SUBDIR install; \ + done; +endef + +override define EMIT_TESTS + echo "./run.sh" +endef + +override define CLEAN + @for DIR in $(SUBDIRS); do \ + BUILD_TARGET=$(OUTPUT)/$$DIR; \ + mkdir $$BUILD_TARGET -p; \ + make OUTPUT=$$BUILD_TARGET -C $$DIR $@;\ + done +endef diff --git a/tools/testing/selftests/android/ion/.gitignore b/tools/testing/selftests/android/ion/.gitignore new file mode 100644 index 000000000000..67e6f391b2a9 --- /dev/null +++ b/tools/testing/selftests/android/ion/.gitignore @@ -0,0 +1,2 @@ +ionapp_export +ionapp_import diff --git a/tools/testing/selftests/android/ion/Makefile b/tools/testing/selftests/android/ion/Makefile new file mode 100644 index 000000000000..96e0c448b39d --- /dev/null +++ b/tools/testing/selftests/android/ion/Makefile @@ -0,0 +1,16 @@ + +INCLUDEDIR := -I. -I../../../../../drivers/staging/android/uapi/ +CFLAGS := $(CFLAGS) $(INCLUDEDIR) -Wall -O2 -g + +TEST_GEN_FILES := ionapp_export ionapp_import + +all: $(TEST_GEN_FILES) + +$(TEST_GEN_FILES): ipcsocket.c ionutils.c + +TEST_PROGS := ion_test.sh + +include ../../lib.mk + +$(OUTPUT)/ionapp_export: ionapp_export.c ipcsocket.c ionutils.c +$(OUTPUT)/ionapp_import: ionapp_import.c ipcsocket.c ionutils.c diff --git a/tools/testing/selftests/android/ion/README b/tools/testing/selftests/android/ion/README new file mode 100644 index 000000000000..21783e9c451e --- /dev/null +++ b/tools/testing/selftests/android/ion/README @@ -0,0 +1,101 @@ +ION BUFFER SHARING UTILITY +========================== +File: ion_test.sh : Utility to test ION driver buffer sharing mechanism. +Author: Pintu Kumar <pintu.ping@gmail.com> + +Introduction: +------------- +This is a test utility to verify ION buffer sharing in user space +between 2 independent processes. +It uses unix domain socket (with SCM_RIGHTS) as IPC to transfer an FD to +another process to share the same buffer. +This utility demonstrates how ION buffer sharing can be implemented between +two user space processes, using various heap types. +The following heap types are supported by ION driver. +ION_HEAP_TYPE_SYSTEM (0) +ION_HEAP_TYPE_SYSTEM_CONTIG (1) +ION_HEAP_TYPE_CARVEOUT (2) +ION_HEAP_TYPE_CHUNK (3) +ION_HEAP_TYPE_DMA (4) + +By default only the SYSTEM and SYSTEM_CONTIG heaps are supported. +Each heap is associated with the respective heap id. +This utility is designed in the form of client/server program. +The server part (ionapp_export) is the exporter of the buffer. +It is responsible for creating an ION client, allocating the buffer based on +the heap id, writing some data to this buffer and then exporting the FD +(associated with this buffer) to another process using socket IPC. +This FD is called as buffer FD (which is different than the ION client FD). + +The client part (ionapp_import) is the importer of the buffer. +It retrives the FD from the socket data and installs into its address space. +This new FD internally points to the same kernel buffer. +So first it reads the data that is stored in this buffer and prints it. +Then it writes the different size of data (it could be different data) to the +same buffer. +Finally the buffer FD must be closed by both the exporter and importer. +Thus the same kernel buffer is shared among two user space processes using +ION driver and only one time allocation. + +Prerequisite: +------------- +This utility works only if /dev/ion interface is present. +The following configs needs to be enabled in kernel to include ion driver. +CONFIG_ANDROID=y +CONFIG_STAGING=y +CONFIG_ION=y +CONFIG_ION_SYSTEM_HEAP=y + +This utility requires to be run as root user. + + +Compile and test: +----------------- +This utility is made to be run as part of kselftest framework in kernel. +To compile and run using kselftest you can simply do the following from the +kernel top directory. +linux$ make TARGETS=android kselftest +Or you can also use: +linux$ make -C tools/testing/selftests TARGETS=android run_tests +Using the selftest it can directly execute the ion_test.sh script to test the +buffer sharing using ion system heap. +Currently the heap size is hard coded as just 10 bytes inside this script. +You need to be a root user to run under selftest. + +You can also compile and test manually using the following steps: +ion$ make +These will generate 2 executable: ionapp_export, ionapp_import +Now you can run the export and import manually by specifying the heap type +and the heap size. +You can also directly execute the shell script to run the test automatically. +Simply use the following command to run the test. +ion$ sudo ./ion_test.sh + +Test Results: +------------- +The utility is verified on Ubuntu-32 bit system with Linux Kernel 4.14. +Here is the snapshot of the test result using kselftest. + +linux# make TARGETS=android kselftest +heap_type: 0, heap_size: 10 +-------------------------------------- +heap type: 0 + heap id: 1 +heap name: ion_system_heap +-------------------------------------- +Fill buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +Sharing fd: 6, Client fd: 5 +<ion_close_buffer_fd>: buffer release successfully.... +Received buffer fd: 4 +Read buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0x0 0x0 0x0 0x0 0x0 0x0 +0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 0x0 +Fill buffer content: +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd 0xfd +0xfd 0xfd +<ion_close_buffer_fd>: buffer release successfully.... +ion_test.sh: heap_type: 0 - [PASS] + +ion_test.sh: done diff --git a/tools/testing/selftests/android/ion/config b/tools/testing/selftests/android/ion/config new file mode 100644 index 000000000000..19db6ca9aa2b --- /dev/null +++ b/tools/testing/selftests/android/ion/config @@ -0,0 +1,4 @@ +CONFIG_ANDROID=y +CONFIG_STAGING=y +CONFIG_ION=y +CONFIG_ION_SYSTEM_HEAP=y diff --git a/tools/testing/selftests/android/ion/ion.h b/tools/testing/selftests/android/ion/ion.h new file mode 100644 index 000000000000..f7021ac51335 --- /dev/null +++ b/tools/testing/selftests/android/ion/ion.h @@ -0,0 +1,143 @@ +/* + * ion.h + * + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +/* This file is copied from drivers/staging/android/uapi/ion.h + * This local copy is required for the selftest to pass, when build + * outside the kernel source tree. + * Please keep this file in sync with its original file until the + * ion driver is moved outside the staging tree. + */ + +#ifndef _UAPI_LINUX_ION_H +#define _UAPI_LINUX_ION_H + +#include <linux/ioctl.h> +#include <linux/types.h> + +/** + * enum ion_heap_types - list of all possible types of heaps + * @ION_HEAP_TYPE_SYSTEM: memory allocated via vmalloc + * @ION_HEAP_TYPE_SYSTEM_CONTIG: memory allocated via kmalloc + * @ION_HEAP_TYPE_CARVEOUT: memory allocated from a prereserved + * carveout heap, allocations are physically + * contiguous + * @ION_HEAP_TYPE_DMA: memory allocated via DMA API + * @ION_NUM_HEAPS: helper for iterating over heaps, a bit mask + * is used to identify the heaps, so only 32 + * total heap types are supported + */ +enum ion_heap_type { + ION_HEAP_TYPE_SYSTEM, + ION_HEAP_TYPE_SYSTEM_CONTIG, + ION_HEAP_TYPE_CARVEOUT, + ION_HEAP_TYPE_CHUNK, + ION_HEAP_TYPE_DMA, + ION_HEAP_TYPE_CUSTOM, /* + * must be last so device specific heaps always + * are at the end of this enum + */ +}; + +#define ION_NUM_HEAP_IDS (sizeof(unsigned int) * 8) + +/** + * allocation flags - the lower 16 bits are used by core ion, the upper 16 + * bits are reserved for use by the heaps themselves. + */ + +/* + * mappings of this buffer should be cached, ion will do cache maintenance + * when the buffer is mapped for dma + */ +#define ION_FLAG_CACHED 1 + +/** + * DOC: Ion Userspace API + * + * create a client by opening /dev/ion + * most operations handled via following ioctls + * + */ + +/** + * struct ion_allocation_data - metadata passed from userspace for allocations + * @len: size of the allocation + * @heap_id_mask: mask of heap ids to allocate from + * @flags: flags passed to heap + * @handle: pointer that will be populated with a cookie to use to + * refer to this allocation + * + * Provided by userspace as an argument to the ioctl + */ +struct ion_allocation_data { + __u64 len; + __u32 heap_id_mask; + __u32 flags; + __u32 fd; + __u32 unused; +}; + +#define MAX_HEAP_NAME 32 + +/** + * struct ion_heap_data - data about a heap + * @name - first 32 characters of the heap name + * @type - heap type + * @heap_id - heap id for the heap + */ +struct ion_heap_data { + char name[MAX_HEAP_NAME]; + __u32 type; + __u32 heap_id; + __u32 reserved0; + __u32 reserved1; + __u32 reserved2; +}; + +/** + * struct ion_heap_query - collection of data about all heaps + * @cnt - total number of heaps to be copied + * @heaps - buffer to copy heap data + */ +struct ion_heap_query { + __u32 cnt; /* Total number of heaps to be copied */ + __u32 reserved0; /* align to 64bits */ + __u64 heaps; /* buffer to be populated */ + __u32 reserved1; + __u32 reserved2; +}; + +#define ION_IOC_MAGIC 'I' + +/** + * DOC: ION_IOC_ALLOC - allocate memory + * + * Takes an ion_allocation_data struct and returns it with the handle field + * populated with the opaque handle for the allocation. + */ +#define ION_IOC_ALLOC _IOWR(ION_IOC_MAGIC, 0, \ + struct ion_allocation_data) + +/** + * DOC: ION_IOC_HEAP_QUERY - information about available heaps + * + * Takes an ion_heap_query structure and populates information about + * available Ion heaps. + */ +#define ION_IOC_HEAP_QUERY _IOWR(ION_IOC_MAGIC, 8, \ + struct ion_heap_query) + +#endif /* _UAPI_LINUX_ION_H */ diff --git a/tools/testing/selftests/android/ion/ion_test.sh b/tools/testing/selftests/android/ion/ion_test.sh new file mode 100755 index 000000000000..a1aff506f5e6 --- /dev/null +++ b/tools/testing/selftests/android/ion/ion_test.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +heapsize=4096 +TCID="ion_test.sh" +errcode=0 + +run_test() +{ + heaptype=$1 + ./ionapp_export -i $heaptype -s $heapsize & + sleep 1 + ./ionapp_import + if [ $? -ne 0 ]; then + echo "$TCID: heap_type: $heaptype - [FAIL]" + errcode=1 + else + echo "$TCID: heap_type: $heaptype - [PASS]" + fi + sleep 1 + echo "" +} + +check_root() +{ + uid=$(id -u) + if [ $uid -ne 0 ]; then + echo $TCID: must be run as root >&2 + exit 0 + fi +} + +check_device() +{ + DEVICE=/dev/ion + if [ ! -e $DEVICE ]; then + echo $TCID: No $DEVICE device found >&2 + echo $TCID: May be CONFIG_ION is not set >&2 + exit 0 + fi +} + +main_function() +{ + check_device + check_root + + # ION_SYSTEM_HEAP TEST + run_test 0 + # ION_SYSTEM_CONTIG_HEAP TEST + run_test 1 +} + +main_function +echo "$TCID: done" +exit $errcode diff --git a/tools/testing/selftests/android/ion/ionapp_export.c b/tools/testing/selftests/android/ion/ionapp_export.c new file mode 100644 index 000000000000..a944e72621a9 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionapp_export.c @@ -0,0 +1,135 @@ +/* + * ionapp_export.c + * + * It is a user space utility to create and export android + * ion memory buffer fd to another process using unix domain socket as IPC. + * This acts like a server for ionapp_import(client). + * So, this server has to be started first before the client. + * + * Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/time.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +void print_usage(int argc, char *argv[]) +{ + printf("Usage: %s [-h <help>] [-i <heap id>] [-s <size in bytes>]\n", + argv[0]); +} + +int main(int argc, char *argv[]) +{ + int opt, ret, status, heapid; + int sockfd, client_fd, shared_fd; + unsigned char *map_buf; + unsigned long map_len, heap_type, heap_size, flags; + struct ion_buffer_info info; + struct socket_info skinfo; + + if (argc < 2) { + print_usage(argc, argv); + return -1; + } + + heap_size = 0; + flags = 0; + + while ((opt = getopt(argc, argv, "hi:s:")) != -1) { + switch (opt) { + case 'h': + print_usage(argc, argv); + exit(0); + break; + case 'i': + heapid = atoi(optarg); + switch (heapid) { + case 0: + heap_type = ION_HEAP_TYPE_SYSTEM; + break; + case 1: + heap_type = ION_HEAP_TYPE_SYSTEM_CONTIG; + break; + default: + printf("ERROR: heap type not supported\n"); + exit(1); + } + break; + case 's': + heap_size = atoi(optarg); + break; + default: + print_usage(argc, argv); + exit(1); + break; + } + } + + if (heap_size <= 0) { + printf("heap_size cannot be 0\n"); + print_usage(argc, argv); + exit(1); + } + + printf("heap_type: %ld, heap_size: %ld\n", heap_type, heap_size); + info.heap_type = heap_type; + info.heap_size = heap_size; + info.flag_type = flags; + + /* This is server: open the socket connection first */ + /* Here; 1 indicates server or exporter */ + status = opensocket(&sockfd, SOCKET_NAME, 1); + if (status < 0) { + fprintf(stderr, "<%s>: Failed opensocket.\n", __func__); + goto err_socket; + } + skinfo.sockfd = sockfd; + + ret = ion_export_buffer_fd(&info); + if (ret < 0) { + fprintf(stderr, "FAILED: ion_get_buffer_fd\n"); + goto err_export; + } + client_fd = info.ionfd; + shared_fd = info.buffd; + map_buf = info.buffer; + map_len = info.buflen; + write_buffer(map_buf, map_len); + + /* share ion buf fd with other user process */ + printf("Sharing fd: %d, Client fd: %d\n", shared_fd, client_fd); + skinfo.datafd = shared_fd; + skinfo.buflen = map_len; + + ret = socket_send_fd(&skinfo); + if (ret < 0) { + fprintf(stderr, "FAILED: socket_send_fd\n"); + goto err_send; + } + +err_send: +err_export: + ion_close_buffer_fd(&info); + +err_socket: + closesocket(sockfd, SOCKET_NAME); + + return 0; +} diff --git a/tools/testing/selftests/android/ion/ionapp_import.c b/tools/testing/selftests/android/ion/ionapp_import.c new file mode 100644 index 000000000000..ae2d704cfa46 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionapp_import.c @@ -0,0 +1,88 @@ +/* + * ionapp_import.c + * + * It is a user space utility to receive android ion memory buffer fd + * over unix domain socket IPC that can be exported by ionapp_export. + * This acts like a client for ionapp_export. + * + * Copyright (C) 2017 Pintu Kumar <pintu.ping@gmail.com> + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +int main(void) +{ + int ret, status; + int sockfd, shared_fd; + unsigned char *map_buf; + unsigned long map_len; + struct ion_buffer_info info; + struct socket_info skinfo; + + /* This is the client part. Here 0 means client or importer */ + status = opensocket(&sockfd, SOCKET_NAME, 0); + if (status < 0) { + fprintf(stderr, "No exporter exists...\n"); + ret = status; + goto err_socket; + } + + skinfo.sockfd = sockfd; + + ret = socket_receive_fd(&skinfo); + if (ret < 0) { + fprintf(stderr, "Failed: socket_receive_fd\n"); + goto err_recv; + } + + shared_fd = skinfo.datafd; + printf("Received buffer fd: %d\n", shared_fd); + if (shared_fd <= 0) { + fprintf(stderr, "ERROR: improper buf fd\n"); + ret = -1; + goto err_fd; + } + + memset(&info, 0, sizeof(info)); + info.buffd = shared_fd; + info.buflen = ION_BUFFER_LEN; + + ret = ion_import_buffer_fd(&info); + if (ret < 0) { + fprintf(stderr, "Failed: ion_use_buffer_fd\n"); + goto err_import; + } + + map_buf = info.buffer; + map_len = info.buflen; + read_buffer(map_buf, map_len); + + /* Write probably new data to the same buffer again */ + map_len = ION_BUFFER_LEN; + write_buffer(map_buf, map_len); + +err_import: + ion_close_buffer_fd(&info); +err_fd: +err_recv: +err_socket: + closesocket(sockfd, SOCKET_NAME); + + return ret; +} diff --git a/tools/testing/selftests/android/ion/ionutils.c b/tools/testing/selftests/android/ion/ionutils.c new file mode 100644 index 000000000000..ce69c14f51fa --- /dev/null +++ b/tools/testing/selftests/android/ion/ionutils.c @@ -0,0 +1,259 @@ +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +//#include <stdint.h> +#include <sys/ioctl.h> +#include <sys/mman.h> +#include "ionutils.h" +#include "ipcsocket.h" + + +void write_buffer(void *buffer, unsigned long len) +{ + int i; + unsigned char *ptr = (unsigned char *)buffer; + + if (!ptr) { + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); + return; + } + + printf("Fill buffer content:\n"); + memset(ptr, 0xfd, len); + for (i = 0; i < len; i++) + printf("0x%x ", ptr[i]); + printf("\n"); +} + +void read_buffer(void *buffer, unsigned long len) +{ + int i; + unsigned char *ptr = (unsigned char *)buffer; + + if (!ptr) { + fprintf(stderr, "<%s>: Invalid buffer...\n", __func__); + return; + } + + printf("Read buffer content:\n"); + for (i = 0; i < len; i++) + printf("0x%x ", ptr[i]); + printf("\n"); +} + +int ion_export_buffer_fd(struct ion_buffer_info *ion_info) +{ + int i, ret, ionfd, buffer_fd; + unsigned int heap_id; + unsigned long maplen; + unsigned char *map_buffer; + struct ion_allocation_data alloc_data; + struct ion_heap_query query; + struct ion_heap_data heap_data[MAX_HEAP_COUNT]; + + if (!ion_info) { + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); + return -1; + } + + /* Create an ION client */ + ionfd = open(ION_DEVICE, O_RDWR); + if (ionfd < 0) { + fprintf(stderr, "<%s>: Failed to open ion client: %s\n", + __func__, strerror(errno)); + return -1; + } + + memset(&query, 0, sizeof(query)); + query.cnt = MAX_HEAP_COUNT; + query.heaps = (unsigned long int)&heap_data[0]; + /* Query ION heap_id_mask from ION heap */ + ret = ioctl(ionfd, ION_IOC_HEAP_QUERY, &query); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed: ION_IOC_HEAP_QUERY: %s\n", + __func__, strerror(errno)); + goto err_query; + } + + heap_id = MAX_HEAP_COUNT + 1; + for (i = 0; i < query.cnt; i++) { + if (heap_data[i].type == ion_info->heap_type) { + printf("--------------------------------------\n"); + printf("heap type: %d\n", heap_data[i].type); + printf(" heap id: %d\n", heap_data[i].heap_id); + printf("heap name: %s\n", heap_data[i].name); + printf("--------------------------------------\n"); + heap_id = heap_data[i].heap_id; + break; + } + } + + if (heap_id > MAX_HEAP_COUNT) { + fprintf(stderr, "<%s>: ERROR: heap type does not exists\n", + __func__); + goto err_heap; + } + + alloc_data.len = ion_info->heap_size; + alloc_data.heap_id_mask = 1 << heap_id; + alloc_data.flags = ion_info->flag_type; + + /* Allocate memory for this ION client as per heap_type */ + ret = ioctl(ionfd, ION_IOC_ALLOC, &alloc_data); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed: ION_IOC_ALLOC: %s\n", + __func__, strerror(errno)); + goto err_alloc; + } + + /* This will return a valid buffer fd */ + buffer_fd = alloc_data.fd; + maplen = alloc_data.len; + + if (buffer_fd < 0 || maplen <= 0) { + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", + __func__, buffer_fd, maplen); + goto err_fd_data; + } + + /* Create memory mapped buffer for the buffer fd */ + map_buffer = (unsigned char *)mmap(NULL, maplen, PROT_READ|PROT_WRITE, + MAP_SHARED, buffer_fd, 0); + if (map_buffer == MAP_FAILED) { + fprintf(stderr, "<%s>: Failed: mmap: %s\n", + __func__, strerror(errno)); + goto err_mmap; + } + + ion_info->ionfd = ionfd; + ion_info->buffd = buffer_fd; + ion_info->buffer = map_buffer; + ion_info->buflen = maplen; + + return 0; + + munmap(map_buffer, maplen); + +err_fd_data: +err_mmap: + /* in case of error: close the buffer fd */ + if (buffer_fd) + close(buffer_fd); + +err_query: +err_heap: +err_alloc: + /* In case of error: close the ion client fd */ + if (ionfd) + close(ionfd); + + return -1; +} + +int ion_import_buffer_fd(struct ion_buffer_info *ion_info) +{ + int buffd; + unsigned char *map_buf; + unsigned long map_len; + + if (!ion_info) { + fprintf(stderr, "<%s>: Invalid ion info\n", __func__); + return -1; + } + + map_len = ion_info->buflen; + buffd = ion_info->buffd; + + if (buffd < 0 || map_len <= 0) { + fprintf(stderr, "<%s>: Invalid map data, fd: %d, len: %ld\n", + __func__, buffd, map_len); + goto err_buffd; + } + + map_buf = (unsigned char *)mmap(NULL, map_len, PROT_READ|PROT_WRITE, + MAP_SHARED, buffd, 0); + if (map_buf == MAP_FAILED) { + printf("<%s>: Failed - mmap: %s\n", + __func__, strerror(errno)); + goto err_mmap; + } + + ion_info->buffer = map_buf; + ion_info->buflen = map_len; + + return 0; + +err_mmap: + if (buffd) + close(buffd); + +err_buffd: + return -1; +} + +void ion_close_buffer_fd(struct ion_buffer_info *ion_info) +{ + if (ion_info) { + /* unmap the buffer properly in the end */ + munmap(ion_info->buffer, ion_info->buflen); + /* close the buffer fd */ + if (ion_info->buffd > 0) + close(ion_info->buffd); + /* Finally, close the client fd */ + if (ion_info->ionfd > 0) + close(ion_info->ionfd); + printf("<%s>: buffer release successfully....\n", __func__); + } +} + +int socket_send_fd(struct socket_info *info) +{ + int status; + int fd, sockfd; + struct socketdata skdata; + + if (!info) { + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); + return -1; + } + + sockfd = info->sockfd; + fd = info->datafd; + memset(&skdata, 0, sizeof(skdata)); + skdata.data = fd; + skdata.len = sizeof(skdata.data); + status = sendtosocket(sockfd, &skdata); + if (status < 0) { + fprintf(stderr, "<%s>: Failed: sendtosocket\n", __func__); + return -1; + } + + return 0; +} + +int socket_receive_fd(struct socket_info *info) +{ + int status; + int fd, sockfd; + struct socketdata skdata; + + if (!info) { + fprintf(stderr, "<%s>: Invalid socket info\n", __func__); + return -1; + } + + sockfd = info->sockfd; + memset(&skdata, 0, sizeof(skdata)); + status = receivefromsocket(sockfd, &skdata); + if (status < 0) { + fprintf(stderr, "<%s>: Failed: receivefromsocket\n", __func__); + return -1; + } + + fd = (int)skdata.data; + info->datafd = fd; + + return status; +} diff --git a/tools/testing/selftests/android/ion/ionutils.h b/tools/testing/selftests/android/ion/ionutils.h new file mode 100644 index 000000000000..9941eb858576 --- /dev/null +++ b/tools/testing/selftests/android/ion/ionutils.h @@ -0,0 +1,55 @@ +#ifndef __ION_UTILS_H +#define __ION_UTILS_H + +#include "ion.h" + +#define SOCKET_NAME "ion_socket" +#define ION_DEVICE "/dev/ion" + +#define ION_BUFFER_LEN 4096 +#define MAX_HEAP_COUNT ION_HEAP_TYPE_CUSTOM + +struct socket_info { + int sockfd; + int datafd; + unsigned long buflen; +}; + +struct ion_buffer_info { + int ionfd; + int buffd; + unsigned int heap_type; + unsigned int flag_type; + unsigned long heap_size; + unsigned long buflen; + unsigned char *buffer; +}; + + +/* This is used to fill the data into the mapped buffer */ +void write_buffer(void *buffer, unsigned long len); + +/* This is used to read the data from the exported buffer */ +void read_buffer(void *buffer, unsigned long len); + +/* This is used to create an ION buffer FD for the kernel buffer + * So you can export this same buffer to others in the form of FD + */ +int ion_export_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to import or map an exported FD. + * So we point to same buffer without making a copy. Hence zero-copy. + */ +int ion_import_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to close all references for the ION client */ +void ion_close_buffer_fd(struct ion_buffer_info *ion_info); + +/* This is used to send FD to another process using socket IPC */ +int socket_send_fd(struct socket_info *skinfo); + +/* This is used to receive FD from another process using socket IPC */ +int socket_receive_fd(struct socket_info *skinfo); + + +#endif diff --git a/tools/testing/selftests/android/ion/ipcsocket.c b/tools/testing/selftests/android/ion/ipcsocket.c new file mode 100644 index 000000000000..7dc521002095 --- /dev/null +++ b/tools/testing/selftests/android/ion/ipcsocket.c @@ -0,0 +1,227 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/un.h> +#include <errno.h> + +#include "ipcsocket.h" + + +int opensocket(int *sockfd, const char *name, int connecttype) +{ + int ret, temp = 1; + + if (!name || strlen(name) > MAX_SOCK_NAME_LEN) { + fprintf(stderr, "<%s>: Invalid socket name.\n", __func__); + return -1; + } + + ret = socket(PF_LOCAL, SOCK_STREAM, 0); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed socket: <%s>\n", + __func__, strerror(errno)); + return ret; + } + + *sockfd = ret; + if (setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, + (char *)&temp, sizeof(int)) < 0) { + fprintf(stderr, "<%s>: Failed setsockopt: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + sprintf(sock_name, "/tmp/%s", name); + + if (connecttype == 1) { + /* This is for Server connection */ + struct sockaddr_un skaddr; + int clientfd; + socklen_t sklen; + + unlink(sock_name); + memset(&skaddr, 0, sizeof(skaddr)); + skaddr.sun_family = AF_LOCAL; + strcpy(skaddr.sun_path, sock_name); + + ret = bind(*sockfd, (struct sockaddr *)&skaddr, + SUN_LEN(&skaddr)); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed bind: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + ret = listen(*sockfd, 5); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed listen: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + memset(&skaddr, 0, sizeof(skaddr)); + sklen = sizeof(skaddr); + + ret = accept(*sockfd, (struct sockaddr *)&skaddr, + (socklen_t *)&sklen); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed accept: <%s>\n", + __func__, strerror(errno)); + goto err; + } + + clientfd = ret; + *sockfd = clientfd; + } else { + /* This is for client connection */ + struct sockaddr_un skaddr; + + memset(&skaddr, 0, sizeof(skaddr)); + skaddr.sun_family = AF_LOCAL; + strcpy(skaddr.sun_path, sock_name); + + ret = connect(*sockfd, (struct sockaddr *)&skaddr, + SUN_LEN(&skaddr)); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed connect: <%s>\n", + __func__, strerror(errno)); + goto err; + } + } + + return 0; + +err: + if (*sockfd) + close(*sockfd); + + return ret; +} + +int sendtosocket(int sockfd, struct socketdata *skdata) +{ + int ret, buffd; + unsigned int len; + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct msghdr msgh; + struct iovec iov; + struct timeval timeout; + fd_set selFDs; + + if (!skdata) { + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); + return -1; + } + + FD_ZERO(&selFDs); + FD_SET(0, &selFDs); + FD_SET(sockfd, &selFDs); + timeout.tv_sec = 20; + timeout.tv_usec = 0; + + ret = select(sockfd+1, NULL, &selFDs, NULL, &timeout); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed select: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + if (FD_ISSET(sockfd, &selFDs)) { + buffd = skdata->data; + len = skdata->len; + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(len); + iov.iov_base = "OK"; + iov.iov_len = 2; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(len); + memcpy(CMSG_DATA(cmsg), &buffd, len); + + ret = sendmsg(sockfd, &msgh, MSG_DONTWAIT); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed sendmsg: <%s>\n", + __func__, strerror(errno)); + return -1; + } + } + + return 0; +} + +int receivefromsocket(int sockfd, struct socketdata *skdata) +{ + int ret, buffd; + unsigned int len = 0; + char cmsg_b[CMSG_SPACE(sizeof(int))]; + struct cmsghdr *cmsg; + struct msghdr msgh; + struct iovec iov; + fd_set recvFDs; + char data[32]; + + if (!skdata) { + fprintf(stderr, "<%s>: socketdata is NULL\n", __func__); + return -1; + } + + FD_ZERO(&recvFDs); + FD_SET(0, &recvFDs); + FD_SET(sockfd, &recvFDs); + + ret = select(sockfd+1, &recvFDs, NULL, NULL, NULL); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed select: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + if (FD_ISSET(sockfd, &recvFDs)) { + len = sizeof(buffd); + memset(&msgh, 0, sizeof(msgh)); + msgh.msg_control = &cmsg_b; + msgh.msg_controllen = CMSG_LEN(len); + iov.iov_base = data; + iov.iov_len = sizeof(data)-1; + msgh.msg_iov = &iov; + msgh.msg_iovlen = 1; + cmsg = CMSG_FIRSTHDR(&msgh); + cmsg->cmsg_level = SOL_SOCKET; + cmsg->cmsg_type = SCM_RIGHTS; + cmsg->cmsg_len = CMSG_LEN(len); + + ret = recvmsg(sockfd, &msgh, MSG_DONTWAIT); + if (ret < 0) { + fprintf(stderr, "<%s>: Failed recvmsg: <%s>\n", + __func__, strerror(errno)); + return -1; + } + + memcpy(&buffd, CMSG_DATA(cmsg), len); + skdata->data = buffd; + skdata->len = len; + } + return 0; +} + +int closesocket(int sockfd, char *name) +{ + char sockname[MAX_SOCK_NAME_LEN]; + + if (sockfd) + close(sockfd); + sprintf(sockname, "/tmp/%s", name); + unlink(sockname); + shutdown(sockfd, 2); + + return 0; +} diff --git a/tools/testing/selftests/android/ion/ipcsocket.h b/tools/testing/selftests/android/ion/ipcsocket.h new file mode 100644 index 000000000000..b3e84498a8a1 --- /dev/null +++ b/tools/testing/selftests/android/ion/ipcsocket.h @@ -0,0 +1,35 @@ + +#ifndef _IPCSOCKET_H +#define _IPCSOCKET_H + + +#define MAX_SOCK_NAME_LEN 64 + +char sock_name[MAX_SOCK_NAME_LEN]; + +/* This structure is responsible for holding the IPC data + * data: hold the buffer fd + * len: just the length of 32-bit integer fd + */ +struct socketdata { + int data; + unsigned int len; +}; + +/* This API is used to open the IPC socket connection + * name: implies a unique socket name in the system + * connecttype: implies server(0) or client(1) + */ +int opensocket(int *sockfd, const char *name, int connecttype); + +/* This is the API to send socket data over IPC socket */ +int sendtosocket(int sockfd, struct socketdata *data); + +/* This is the API to receive socket data over IPC socket */ +int receivefromsocket(int sockfd, struct socketdata *data); + +/* This is the API to close the socket connection */ +int closesocket(int sockfd, char *name); + + +#endif diff --git a/tools/testing/selftests/android/run.sh b/tools/testing/selftests/android/run.sh new file mode 100755 index 000000000000..dd8edf291454 --- /dev/null +++ b/tools/testing/selftests/android/run.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +(cd ion; ./ion_test.sh) diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index bf092b83e453..3c64f30cf63c 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c @@ -4377,11 +4377,10 @@ static struct bpf_test tests[] = { BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), - BPF_MOV64_IMM(BPF_REG_1, 0), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_write_user), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 3), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_EMIT_CALL(BPF_FUNC_trace_printk), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, @@ -4481,14 +4480,12 @@ static struct bpf_test tests[] = { BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, offsetof(struct test_val, foo)), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), - BPF_MOV64_IMM(BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_write_user), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_EMIT_CALL(BPF_FUNC_trace_printk), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, @@ -4618,18 +4615,16 @@ static struct bpf_test tests[] = { BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_MOV64_IMM(BPF_REG_3, 0), BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), - BPF_MOV64_IMM(BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_write_user), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_EMIT_CALL(BPF_FUNC_trace_printk), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, - .errstr = "R2 min value is outside of the array range", + .errstr = "R1 min value is outside of the array range", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -4760,20 +4755,18 @@ static struct bpf_test tests[] = { BPF_ST_MEM(BPF_DW, BPF_REG_2, 0, 0), BPF_LD_MAP_FD(BPF_REG_1, 0), BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), - BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 7), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_0, 0), BPF_JMP_IMM(BPF_JGT, BPF_REG_3, - offsetof(struct test_val, foo), 4), + offsetof(struct test_val, foo), 3), BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_3), - BPF_MOV64_REG(BPF_REG_2, BPF_REG_1), - BPF_MOV64_IMM(BPF_REG_1, 0), - BPF_MOV64_IMM(BPF_REG_3, 0), - BPF_EMIT_CALL(BPF_FUNC_probe_write_user), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_EMIT_CALL(BPF_FUNC_trace_printk), BPF_EXIT_INSN(), }, .fixup_map2 = { 3 }, - .errstr = "R2 min value is outside of the array range", + .errstr = "R1 min value is outside of the array range", .result = REJECT, .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, @@ -5638,7 +5631,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_TRACEPOINT, }, { - "helper access to variable memory: size = 0 allowed on NULL", + "helper access to variable memory: size = 0 allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_MOV64_IMM(BPF_REG_1, 0), BPF_MOV64_IMM(BPF_REG_2, 0), @@ -5652,7 +5645,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size > 0 not allowed on NULL", + "helper access to variable memory: size > 0 not allowed on NULL (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_MOV64_IMM(BPF_REG_1, 0), BPF_MOV64_IMM(BPF_REG_2, 0), @@ -5670,7 +5663,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size = 0 allowed on != NULL stack pointer", + "helper access to variable memory: size = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), @@ -5687,7 +5680,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size = 0 allowed on != NULL map pointer", + "helper access to variable memory: size = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5709,7 +5702,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer", + "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5734,7 +5727,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size possible = 0 allowed on != NULL map pointer", + "helper access to variable memory: size possible = 0 allowed on != NULL map pointer (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), @@ -5757,7 +5750,7 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { - "helper access to variable memory: size possible = 0 allowed on != NULL packet pointer", + "helper access to variable memory: size possible = 0 allowed on != NULL packet pointer (ARG_PTR_TO_MEM_OR_NULL)", .insns = { BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_1, offsetof(struct __sk_buff, data)), @@ -5779,6 +5772,105 @@ static struct bpf_test tests[] = { .prog_type = BPF_PROG_TYPE_SCHED_CLS, }, { + "helper access to variable memory: size = 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .errstr = "R1 type=inv expected=fp", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size > 0 not allowed on NULL (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_MOV64_IMM(BPF_REG_1, 0), + BPF_MOV64_IMM(BPF_REG_2, 1), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .errstr = "R1 type=inv expected=fp", + .result = REJECT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_MOV64_IMM(BPF_REG_2, 0), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size possible = 0 allowed on != NULL stack pointer (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 4), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { + "helper access to variable memory: size possible = 0 allowed on != NULL map pointer (!ARG_PTR_TO_MEM_OR_NULL)", + .insns = { + BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), + BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), + BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), + BPF_LD_MAP_FD(BPF_REG_1, 0), + BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), + BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5), + BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), + BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_0, 0), + BPF_JMP_IMM(BPF_JGT, BPF_REG_2, 8, 2), + BPF_MOV64_IMM(BPF_REG_3, 0), + BPF_EMIT_CALL(BPF_FUNC_probe_read), + BPF_EXIT_INSN(), + }, + .fixup_map1 = { 3 }, + .result = ACCEPT, + .prog_type = BPF_PROG_TYPE_TRACEPOINT, + }, + { "helper access to variable memory: 8 bytes leak", .insns = { BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), diff --git a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c index 960d02100c26..2d95e5adde72 100644 --- a/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c +++ b/tools/testing/selftests/breakpoints/breakpoint_test_arm64.c @@ -19,6 +19,7 @@ #define _GNU_SOURCE +#include <asm/ptrace.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/ptrace.h> diff --git a/tools/testing/selftests/cpu-hotplug/config b/tools/testing/selftests/cpu-hotplug/config index e6ab090cfbf3..d4aca2ad5069 100644 --- a/tools/testing/selftests/cpu-hotplug/config +++ b/tools/testing/selftests/cpu-hotplug/config @@ -1,2 +1 @@ CONFIG_NOTIFIER_ERROR_INJECTION=y -CONFIG_CPU_NOTIFIER_ERROR_INJECT=m diff --git a/tools/testing/selftests/exec/execveat.c b/tools/testing/selftests/exec/execveat.c index 8d5d1d2ee7c1..67cd4597db2b 100644 --- a/tools/testing/selftests/exec/execveat.c +++ b/tools/testing/selftests/exec/execveat.c @@ -147,7 +147,7 @@ static void exe_cp(const char *src, const char *dest) } #define XX_DIR_LEN 200 -static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) +static int check_execveat_pathmax(int root_dfd, const char *src, int is_script) { int fail = 0; int ii, count, len; @@ -156,20 +156,30 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) if (*longpath == '\0') { /* Create a filename close to PATH_MAX in length */ + char *cwd = getcwd(NULL, 0); + + if (!cwd) { + printf("Failed to getcwd(), errno=%d (%s)\n", + errno, strerror(errno)); + return 2; + } + strcpy(longpath, cwd); + strcat(longpath, "/"); memset(longname, 'x', XX_DIR_LEN - 1); longname[XX_DIR_LEN - 1] = '/'; longname[XX_DIR_LEN] = '\0'; - count = (PATH_MAX - 3) / XX_DIR_LEN; + count = (PATH_MAX - 3 - strlen(cwd)) / XX_DIR_LEN; for (ii = 0; ii < count; ii++) { strcat(longpath, longname); mkdir(longpath, 0755); } - len = (PATH_MAX - 3) - (count * XX_DIR_LEN); + len = (PATH_MAX - 3 - strlen(cwd)) - (count * XX_DIR_LEN); if (len <= 0) len = 1; memset(longname, 'y', len); longname[len] = '\0'; strcat(longpath, longname); + free(cwd); } exe_cp(src, longpath); @@ -190,7 +200,7 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) } /* - * Execute as a long pathname relative to ".". If this is a script, + * Execute as a long pathname relative to "/". If this is a script, * the interpreter will launch but fail to open the script because its * name ("/dev/fd/5/xxx....") is bigger than PATH_MAX. * @@ -200,10 +210,10 @@ static int check_execveat_pathmax(int dot_dfd, const char *src, int is_script) * the exit status shall be 126."), so allow either. */ if (is_script) - fail += check_execveat_invoked_rc(dot_dfd, longpath, 0, + fail += check_execveat_invoked_rc(root_dfd, longpath + 1, 0, 127, 126); else - fail += check_execveat(dot_dfd, longpath, 0); + fail += check_execveat(root_dfd, longpath + 1, 0); return fail; } @@ -218,6 +228,7 @@ static int run_tests(void) int subdir_dfd_ephemeral = open_or_die("subdir.ephemeral", O_DIRECTORY|O_RDONLY); int dot_dfd = open_or_die(".", O_DIRECTORY|O_RDONLY); + int root_dfd = open_or_die("/", O_DIRECTORY|O_RDONLY); int dot_dfd_path = open_or_die(".", O_DIRECTORY|O_RDONLY|O_PATH); int dot_dfd_cloexec = open_or_die(".", O_DIRECTORY|O_RDONLY|O_CLOEXEC); int fd = open_or_die("execveat", O_RDONLY); @@ -353,8 +364,8 @@ static int run_tests(void) /* Attempt to execute relative to non-directory => ENOTDIR */ fail += check_execveat_fail(fd, "execveat", 0, ENOTDIR); - fail += check_execveat_pathmax(dot_dfd, "execveat", 0); - fail += check_execveat_pathmax(dot_dfd, "script", 1); + fail += check_execveat_pathmax(root_dfd, "execveat", 0); + fail += check_execveat_pathmax(root_dfd, "script", 1); return fail; } diff --git a/tools/testing/selftests/firmware/fw_fallback.sh b/tools/testing/selftests/firmware/fw_fallback.sh index a52a3bab532b..34a42c68ebfb 100755 --- a/tools/testing/selftests/firmware/fw_fallback.sh +++ b/tools/testing/selftests/firmware/fw_fallback.sh @@ -86,6 +86,11 @@ load_fw_cancel() load_fw_custom() { + if [ ! -e "$DIR"/trigger_custom_fallback ]; then + echo "$0: custom fallback trigger not present, ignoring test" >&2 + return 1 + fi + local name="$1" local file="$2" @@ -108,11 +113,17 @@ load_fw_custom() # Wait for request to finish. wait + return 0 } load_fw_custom_cancel() { + if [ ! -e "$DIR"/trigger_custom_fallback ]; then + echo "$0: canceling custom fallback trigger not present, ignoring test" >&2 + return 1 + fi + local name="$1" local file="$2" @@ -133,6 +144,7 @@ load_fw_custom_cancel() # Wait for request to finish. wait + return 0 } load_fw_fallback_with_child() @@ -227,20 +239,22 @@ else echo "$0: cancelling fallback mechanism works" fi -load_fw_custom "$NAME" "$FW" -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded" >&2 - exit 1 -else - echo "$0: custom fallback loading mechanism works" +if load_fw_custom "$NAME" "$FW" ; then + if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded" >&2 + exit 1 + else + echo "$0: custom fallback loading mechanism works" + fi fi -load_fw_custom_cancel "nope-$NAME" "$FW" -if diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was expected to be cancelled" >&2 - exit 1 -else - echo "$0: cancelling custom fallback mechanism works" +if load_fw_custom_cancel "nope-$NAME" "$FW" ; then + if diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was expected to be cancelled" >&2 + exit 1 + else + echo "$0: cancelling custom fallback mechanism works" + fi fi set +e diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 62f2d6f54929..b1f20fef36c7 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -70,9 +70,13 @@ if printf '\000' >"$DIR"/trigger_request 2> /dev/null; then exit 1 fi -if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then - echo "$0: empty filename should not succeed (async)" >&2 - exit 1 +if [ ! -e "$DIR"/trigger_async_request ]; then + echo "$0: empty filename: async trigger not present, ignoring test" >&2 +else + if printf '\000' >"$DIR"/trigger_async_request 2> /dev/null; then + echo "$0: empty filename should not succeed (async)" >&2 + exit 1 + fi fi # Request a firmware that doesn't exist, it should fail. @@ -105,17 +109,21 @@ else fi # Try the asynchronous version too -if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then - echo "$0: could not trigger async request" >&2 - exit 1 -fi - -# Verify the contents are what we expect. -if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then - echo "$0: firmware was not loaded (async)" >&2 - exit 1 +if [ ! -e "$DIR"/trigger_async_request ]; then + echo "$0: firmware loading: async trigger not present, ignoring test" >&2 else - echo "$0: async filesystem loading works" + if ! echo -n "$NAME" >"$DIR"/trigger_async_request ; then + echo "$0: could not trigger async request" >&2 + exit 1 + fi + + # Verify the contents are what we expect. + if ! diff -q "$FW" /dev/test_firmware >/dev/null ; then + echo "$0: firmware was not loaded (async)" >&2 + exit 1 + else + echo "$0: async filesystem loading works" + fi fi ### Batched requests tests diff --git a/tools/testing/selftests/ftrace/config b/tools/testing/selftests/ftrace/config index 8a1c9f949fe0..b01924c71c09 100644 --- a/tools/testing/selftests/ftrace/config +++ b/tools/testing/selftests/ftrace/config @@ -1,2 +1,6 @@ CONFIG_KPROBES=y CONFIG_FTRACE=y +CONFIG_FUNCTION_PROFILER=y +CONFIG_TRACER_SNAPSHOT=y +CONFIG_STACK_TRACER=y +CONFIG_HIST_TRIGGERS=y diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index abc706cf7702..f9a9d424c980 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -222,7 +222,14 @@ SIG_RESULT= SIG_BASE=36 # Use realtime signals SIG_PID=$$ +exit_pass () { + exit 0 +} + SIG_FAIL=$((SIG_BASE + FAIL)) +exit_fail () { + exit 1 +} trap 'SIG_RESULT=$FAIL' $SIG_FAIL SIG_UNRESOLVED=$((SIG_BASE + UNRESOLVED)) diff --git a/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc index aa51f6c17359..0696098d6408 100644 --- a/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc +++ b/tools/testing/selftests/ftrace/test.d/00basic/basic4.tc @@ -2,4 +2,4 @@ # description: Basic event tracing check test -f available_events -a -f set_event -a -d events # check scheduler events are available -grep -q sched available_events && exit 0 || exit $FAIL +grep -q sched available_events && exit_pass || exit_fail diff --git a/tools/testing/selftests/ftrace/test.d/event/event-enable.tc b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc index 6ff851a75884..9daf034186f5 100644 --- a/tools/testing/selftests/ftrace/test.d/event/event-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/event-enable.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/event-pid.tc b/tools/testing/selftests/ftrace/test.d/event/event-pid.tc index cc14feec6e1f..132478b305c2 100644 --- a/tools/testing/selftests/ftrace/test.d/event/event-pid.tc +++ b/tools/testing/selftests/ftrace/test.d/event/event-pid.tc @@ -13,7 +13,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc index 85094904aa79..6a37a8642ee6 100644 --- a/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/subsystem-enable.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc index cc1cf4d30ef5..4e9b6e2c0219 100644 --- a/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc +++ b/tools/testing/selftests/ftrace/test.d/event/toplevel-enable.tc @@ -10,7 +10,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc index 45df747887e0..1aec99d108eb 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter-stack.tc @@ -28,7 +28,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } disable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc index 0387e22e7577..9f8d27ca39cf 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/fgraph-filter.tc @@ -18,7 +18,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } disable_tracing diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc index 78524fcc25ae..524ce24b3c22 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func-filter-pid.tc @@ -51,7 +51,7 @@ do_reset() { fail() { # msg do_reset echo $1 - exit $FAIL + exit_fail } yield() { diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc index 9d4afcca1e36..6fed4cf2db81 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_event_triggers.tc @@ -27,7 +27,7 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } SLEEP_TIME=".1" @@ -48,8 +48,7 @@ test_event_enabled() { e=`cat $EVENT_ENABLE` if [ "$e" != $val ]; then - echo "Expected $val but found $e" - exit 1 + fail "Expected $val but found $e" fi } diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc index fe0dc5a7ea26..b2d5a8febfe8 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_profiler.tc @@ -32,7 +32,7 @@ fail() { # mesg reset_tracer echo > set_ftrace_filter echo $1 - exit $FAIL + exit_fail } echo "Testing function tracer with profiler:" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc index 5ad723724adb..0f3f92622e33 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_set_ftrace_file.tc @@ -26,14 +26,14 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } do_reset FILTER=set_ftrace_filter FUNC1="schedule" -FUNC2="do_IRQ" +FUNC2="do_softirq" ALL_FUNCS="#### all functions enabled ####" diff --git a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc index cdc92a371cd7..f6d9ac73268a 100644 --- a/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc +++ b/tools/testing/selftests/ftrace/test.d/ftrace/func_traceonoff_triggers.tc @@ -27,7 +27,7 @@ do_reset() { fail() { # mesg do_reset echo $1 - exit $FAIL + exit_fail } SLEEP_TIME=".1" diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index d7f48b55df51..4fa0f79144f4 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -11,7 +11,7 @@ fail() { # mesg rmdir foo 2>/dev/null echo $1 set -e - exit $FAIL + exit_fail } cd instances diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance.tc b/tools/testing/selftests/ftrace/test.d/instances/instance.tc index ddda62203366..b84651283bf3 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance.tc @@ -11,7 +11,7 @@ fail() { # mesg rmdir x y z 2>/dev/null echo $1 set -e - exit $FAIL + exit_fail } cd instances diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc index 0e6f415c6152..bbc443a9190c 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/busy_check.tc @@ -9,7 +9,7 @@ echo > kprobe_events echo p:myevent _do_fork > kprobe_events test -d events/kprobes/myevent echo 1 > events/kprobes/myevent/enable -echo > kprobe_events && exit 1 # this must fail +echo > kprobe_events && exit_fail # this must fail echo 0 > events/kprobes/myevent/enable echo > kprobe_events # this must succeed clear_trace diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc index 679bbd23bcc3..8b43c6804fc3 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args.tc @@ -14,5 +14,5 @@ echo 1 > events/kprobes/testprobe/enable echo 0 > events/kprobes/testprobe/enable echo "-:testprobe" >> kprobe_events clear_trace -test -d events/kprobes/testprobe && exit 1 || exit 0 +test -d events/kprobes/testprobe && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc index 17d33ba192f6..2a1755bfc290 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kprobe_args_type.tc @@ -35,4 +35,4 @@ check_types $ARGS echo "-:testprobe" >> kprobe_events clear_trace -test -d events/kprobes/testprobe && exit 1 || exit 0 +test -d events/kprobes/testprobe && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc index f1825bdbe3f3..321954683aaa 100644 --- a/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc +++ b/tools/testing/selftests/ftrace/test.d/kprobe/kretprobe_args.tc @@ -14,4 +14,4 @@ echo 1 > events/kprobes/testprobe2/enable echo 0 > events/kprobes/testprobe2/enable echo '-:testprobe2' >> kprobe_events clear_trace -test -d events/kprobes/testprobe2 && exit 1 || exit 0 +test -d events/kprobes/testprobe2 && exit_fail || exit_pass diff --git a/tools/testing/selftests/ftrace/test.d/template b/tools/testing/selftests/ftrace/test.d/template index 5448f7abad5f..5c39ceb18a0d 100644 --- a/tools/testing/selftests/ftrace/test.d/template +++ b/tools/testing/selftests/ftrace/test.d/template @@ -4,6 +4,7 @@ # Note that all tests are run with "errexit" option. exit 0 # Return 0 if the test is passed, otherwise return !0 +# Or you can call exit_pass for passed test, and exit_fail for failed test. # If the test could not run because of lack of feature, call exit_unsupported # If the test returned unclear results, call exit_unresolved # If the test is a dummy, or a placeholder, call exit_untested diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc index 839ac4320b24..28cc355a3a7b 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-eventonoff.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc index 66873c4b12c9..a48e23eb8a8b 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-filter.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc index 4237b32769f1..8da80efc44d8 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist-mod.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc index d24e2b8bd863..449fe9ff91a2 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-hist.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc index 4c0774fff378..c5ef8b9d02b3 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-multihist.tc @@ -12,7 +12,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc index 3fc6321e081f..ed38f0050d77 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-snapshot.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc index 3652824f81ed..3121d795a868 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-stacktrace.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc b/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc index 6d9051cdf408..c59d9eb546da 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/trigger-traceonoff.tc @@ -11,7 +11,7 @@ do_reset() { fail() { #msg do_reset echo $1 - exit $FAIL + exit_fail } if [ ! -f set_event -o ! -d events/sched ]; then diff --git a/tools/testing/selftests/memfd/memfd_test.c b/tools/testing/selftests/memfd/memfd_test.c index 845e5f67b6f0..132a54f74e88 100644 --- a/tools/testing/selftests/memfd/memfd_test.c +++ b/tools/testing/selftests/memfd/memfd_test.c @@ -515,7 +515,7 @@ static void mfd_assert_grow_write(int fd) buf = malloc(mfd_def_size * 8); if (!buf) { - printf("malloc(%d) failed: %m\n", mfd_def_size * 8); + printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); abort(); } @@ -535,7 +535,7 @@ static void mfd_fail_grow_write(int fd) buf = malloc(mfd_def_size * 8); if (!buf) { - printf("malloc(%d) failed: %m\n", mfd_def_size * 8); + printf("malloc(%zu) failed: %m\n", mfd_def_size * 8); abort(); } diff --git a/tools/testing/selftests/memory-hotplug/Makefile b/tools/testing/selftests/memory-hotplug/Makefile index 23db11c94b59..86636d207adf 100644 --- a/tools/testing/selftests/memory-hotplug/Makefile +++ b/tools/testing/selftests/memory-hotplug/Makefile @@ -4,10 +4,10 @@ all: include ../lib.mk TEST_PROGS := mem-on-off-test.sh -override RUN_TESTS := ./mem-on-off-test.sh -r 2 || echo "selftests: memory-hotplug [FAIL]" +override RUN_TESTS := ./mem-on-off-test.sh -r 2 && echo "selftests: memory-hotplug [PASS]" || echo "selftests: memory-hotplug [FAIL]" override EMIT_TESTS := echo "$(RUN_TESTS)" run_full_test: - @/bin/bash ./mem-on-off-test.sh || echo "memory-hotplug selftests: [FAIL]" + @/bin/bash ./mem-on-off-test.sh && echo "memory-hotplug selftests: [PASS]" || echo "memory-hotplug selftests: [FAIL]" clean: diff --git a/tools/testing/selftests/seccomp/.gitignore b/tools/testing/selftests/seccomp/.gitignore index 346d83ca8069..5af29d3a1b0a 100644 --- a/tools/testing/selftests/seccomp/.gitignore +++ b/tools/testing/selftests/seccomp/.gitignore @@ -1 +1,2 @@ seccomp_bpf +seccomp_benchmark diff --git a/tools/testing/selftests/timers/.gitignore b/tools/testing/selftests/timers/.gitignore index cc986621f512..2c8ac8416299 100644 --- a/tools/testing/selftests/timers/.gitignore +++ b/tools/testing/selftests/timers/.gitignore @@ -18,3 +18,5 @@ threadtest valid-adjtimex adjtick set-tz +freq-step +rtctest_setdate diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c index 8daeb7d7032c..2df26bd0099c 100644 --- a/tools/testing/selftests/vDSO/vdso_test.c +++ b/tools/testing/selftests/vDSO/vdso_test.c @@ -19,6 +19,19 @@ extern void *vdso_sym(const char *version, const char *name); extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); extern void vdso_init_from_auxv(void *auxv); +/* + * ARM64's vDSO exports its gettimeofday() implementation with a different + * name and version from other architectures, so we need to handle it as + * a special case. + */ +#if defined(__aarch64__) +const char *version = "LINUX_2.6.39"; +const char *name = "__kernel_gettimeofday"; +#else +const char *version = "LINUX_2.6"; +const char *name = "__vdso_gettimeofday"; +#endif + int main(int argc, char **argv) { unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); @@ -31,10 +44,10 @@ int main(int argc, char **argv) /* Find gettimeofday. */ typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); - gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); + gtod_t gtod = (gtod_t)vdso_sym(version, name); if (!gtod) { - printf("Could not find __vdso_gettimeofday\n"); + printf("Could not find %s\n", name); return 1; } @@ -45,7 +58,7 @@ int main(int argc, char **argv) printf("The time is %lld.%06lld\n", (long long)tv.tv_sec, (long long)tv.tv_usec); } else { - printf("__vdso_gettimeofday failed\n"); + printf("%s failed\n", name); } return 0; diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index 142c565bb351..1ca2ee4d15b9 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore @@ -8,3 +8,5 @@ on-fault-limit transhuge-stress userfaultfd mlock-intersect-test +mlock-random-test +virtual_address_range diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index e49eca1915f8..7f45806bd863 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile @@ -18,6 +18,7 @@ TEST_GEN_FILES += transhuge-stress TEST_GEN_FILES += userfaultfd TEST_GEN_FILES += mlock-random-test TEST_GEN_FILES += virtual_address_range +TEST_GEN_FILES += gup_benchmark TEST_PROGS := run_vmtests diff --git a/tools/testing/selftests/vm/gup_benchmark.c b/tools/testing/selftests/vm/gup_benchmark.c new file mode 100644 index 000000000000..36df55132036 --- /dev/null +++ b/tools/testing/selftests/vm/gup_benchmark.c @@ -0,0 +1,91 @@ +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <sys/ioctl.h> +#include <sys/mman.h> +#include <sys/prctl.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <linux/types.h> + +#define MB (1UL << 20) +#define PAGE_SIZE sysconf(_SC_PAGESIZE) + +#define GUP_FAST_BENCHMARK _IOWR('g', 1, struct gup_benchmark) + +struct gup_benchmark { + __u64 delta_usec; + __u64 addr; + __u64 size; + __u32 nr_pages_per_call; + __u32 flags; +}; + +int main(int argc, char **argv) +{ + struct gup_benchmark gup; + unsigned long size = 128 * MB; + int i, fd, opt, nr_pages = 1, thp = -1, repeats = 1, write = 0; + char *p; + + while ((opt = getopt(argc, argv, "m:r:n:tT")) != -1) { + switch (opt) { + case 'm': + size = atoi(optarg) * MB; + break; + case 'r': + repeats = atoi(optarg); + break; + case 'n': + nr_pages = atoi(optarg); + break; + case 't': + thp = 1; + break; + case 'T': + thp = 0; + break; + case 'w': + write = 1; + default: + return -1; + } + } + + gup.nr_pages_per_call = nr_pages; + gup.flags = write; + + fd = open("/sys/kernel/debug/gup_benchmark", O_RDWR); + if (fd == -1) + perror("open"), exit(1); + + p = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (p == MAP_FAILED) + perror("mmap"), exit(1); + gup.addr = (unsigned long)p; + + if (thp == 1) + madvise(p, size, MADV_HUGEPAGE); + else if (thp == 0) + madvise(p, size, MADV_NOHUGEPAGE); + + for (; (unsigned long)p < gup.addr + size; p += PAGE_SIZE) + p[0] = 0; + + for (i = 0; i < repeats; i++) { + gup.size = size; + if (ioctl(fd, GUP_FAST_BENCHMARK, &gup)) + perror("ioctl"), exit(1); + + printf("Time: %lld us", gup.delta_usec); + if (gup.size != size) + printf(", truncated (size: %lld)", gup.size); + printf("\n"); + } + + return 0; +} diff --git a/tools/testing/selftests/x86/5lvl.c b/tools/testing/selftests/x86/5lvl.c new file mode 100644 index 000000000000..2eafdcd4c2b3 --- /dev/null +++ b/tools/testing/selftests/x86/5lvl.c @@ -0,0 +1,177 @@ +#include <stdio.h> +#include <sys/mman.h> + +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) + +#define PAGE_SIZE 4096 +#define LOW_ADDR ((void *) (1UL << 30)) +#define HIGH_ADDR ((void *) (1UL << 50)) + +struct testcase { + void *addr; + unsigned long size; + unsigned long flags; + const char *msg; + unsigned int low_addr_required:1; + unsigned int keep_mapped:1; +}; + +static struct testcase testcases[] = { + { + .addr = NULL, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(NULL)", + .low_addr_required = 1, + }, + { + .addr = LOW_ADDR, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(LOW_ADDR)", + .low_addr_required = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(HIGH_ADDR)", + .keep_mapped = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(HIGH_ADDR) again", + .keep_mapped = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + .msg = "mmap(HIGH_ADDR, MAP_FIXED)", + }, + { + .addr = (void*) -1, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(-1)", + .keep_mapped = 1, + }, + { + .addr = (void*) -1, + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(-1) again", + }, + { + .addr = (void *)((1UL << 47) - PAGE_SIZE), + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap((1UL << 47), 2 * PAGE_SIZE)", + .low_addr_required = 1, + .keep_mapped = 1, + }, + { + .addr = (void *)((1UL << 47) - PAGE_SIZE / 2), + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap((1UL << 47), 2 * PAGE_SIZE / 2)", + .low_addr_required = 1, + .keep_mapped = 1, + }, + { + .addr = (void *)((1UL << 47) - PAGE_SIZE), + .size = 2 * PAGE_SIZE, + .flags = MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + .msg = "mmap((1UL << 47) - PAGE_SIZE, 2 * PAGE_SIZE, MAP_FIXED)", + }, + { + .addr = NULL, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(NULL, MAP_HUGETLB)", + .low_addr_required = 1, + }, + { + .addr = LOW_ADDR, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(LOW_ADDR, MAP_HUGETLB)", + .low_addr_required = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(HIGH_ADDR, MAP_HUGETLB)", + .keep_mapped = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(HIGH_ADDR, MAP_HUGETLB) again", + .keep_mapped = 1, + }, + { + .addr = HIGH_ADDR, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + .msg = "mmap(HIGH_ADDR, MAP_FIXED | MAP_HUGETLB)", + }, + { + .addr = (void*) -1, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(-1, MAP_HUGETLB)", + .keep_mapped = 1, + }, + { + .addr = (void*) -1, + .size = 2UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap(-1, MAP_HUGETLB) again", + }, + { + .addr = (void *)((1UL << 47) - PAGE_SIZE), + .size = 4UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS, + .msg = "mmap((1UL << 47), 4UL << 20, MAP_HUGETLB)", + .low_addr_required = 1, + .keep_mapped = 1, + }, + { + .addr = (void *)((1UL << 47) - (2UL << 20)), + .size = 4UL << 20, + .flags = MAP_HUGETLB | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, + .msg = "mmap((1UL << 47) - (2UL << 20), 4UL << 20, MAP_FIXED | MAP_HUGETLB)", + }, +}; + +int main(int argc, char **argv) +{ + int i; + void *p; + + for (i = 0; i < ARRAY_SIZE(testcases); i++) { + struct testcase *t = testcases + i; + + p = mmap(t->addr, t->size, PROT_NONE, t->flags, -1, 0); + + printf("%s: %p - ", t->msg, p); + + if (p == MAP_FAILED) { + printf("FAILED\n"); + continue; + } + + if (t->low_addr_required && p >= (void *)(1UL << 47)) + printf("FAILED\n"); + else + printf("OK\n"); + if (!t->keep_mapped) + munmap(p, t->size); + } + return 0; +} diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 7b1adeee4b0f..939a337128db 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -11,7 +11,7 @@ TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_sysc TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer -TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip +TARGETS_C_64BIT_ONLY := fsgsbase sysret_rip 5lvl TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) diff --git a/tools/testing/selftests/x86/mpx-hw.h b/tools/testing/selftests/x86/mpx-hw.h index 3f0093911f03..d1b61ab870f8 100644 --- a/tools/testing/selftests/x86/mpx-hw.h +++ b/tools/testing/selftests/x86/mpx-hw.h @@ -52,14 +52,14 @@ struct mpx_bd_entry { union { char x[MPX_BOUNDS_DIR_ENTRY_SIZE_BYTES]; - void *contents[1]; + void *contents[0]; }; } __attribute__((packed)); struct mpx_bt_entry { union { char x[MPX_BOUNDS_TABLE_ENTRY_SIZE_BYTES]; - unsigned long contents[1]; + unsigned long contents[0]; }; } __attribute__((packed)); diff --git a/tools/testing/selftests/x86/pkey-helpers.h b/tools/testing/selftests/x86/pkey-helpers.h index 3818f25391c2..b3cb7670e026 100644 --- a/tools/testing/selftests/x86/pkey-helpers.h +++ b/tools/testing/selftests/x86/pkey-helpers.h @@ -30,6 +30,7 @@ static inline void sigsafe_printf(const char *format, ...) if (!dprint_in_signal) { vprintf(format, ap); } else { + int ret; int len = vsnprintf(dprint_in_signal_buffer, DPRINT_IN_SIGNAL_BUF_SIZE, format, ap); @@ -39,7 +40,9 @@ static inline void sigsafe_printf(const char *format, ...) */ if (len > DPRINT_IN_SIGNAL_BUF_SIZE) len = DPRINT_IN_SIGNAL_BUF_SIZE; - write(1, dprint_in_signal_buffer, len); + ret = write(1, dprint_in_signal_buffer, len); + if (ret < 0) + abort(); } va_end(ap); } diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c index 7a1cc0e56d2d..bc1b0735bb50 100644 --- a/tools/testing/selftests/x86/protection_keys.c +++ b/tools/testing/selftests/x86/protection_keys.c @@ -250,7 +250,7 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) unsigned long ip; char *fpregs; u32 *pkru_ptr; - u64 si_pkey; + u64 siginfo_pkey; u32 *si_pkey_ptr; int pkru_offset; fpregset_t fpregset; @@ -292,9 +292,9 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset); dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr); dump_mem(si_pkey_ptr - 8, 24); - si_pkey = *si_pkey_ptr; - pkey_assert(si_pkey < NR_PKEYS); - last_si_pkey = si_pkey; + siginfo_pkey = *si_pkey_ptr; + pkey_assert(siginfo_pkey < NR_PKEYS); + last_si_pkey = siginfo_pkey; if ((si->si_code == SEGV_MAPERR) || (si->si_code == SEGV_ACCERR) || @@ -306,7 +306,7 @@ void signal_handler(int signum, siginfo_t *si, void *vucontext) dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr); /* need __rdpkru() version so we do not do shadow_pkru checking */ dprintf1("signal pkru from pkru: %08x\n", __rdpkru()); - dprintf1("si_pkey from siginfo: %jx\n", si_pkey); + dprintf1("pkey from siginfo: %jx\n", siginfo_pkey); *(u64 *)pkru_ptr = 0x00000000; dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n"); pkru_faults++; |