| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Benjamin Marzinski <bmarzins@redhat.com> |
| Date: Wed, 13 Apr 2022 23:27:37 -0500 |
| Subject: [PATCH] libmultipath: add a protocol subsection to multipath.conf |
| |
| Some storage arrays can be accessed using multiple protocols at the same |
| time. In these cases, users may want to set path attributes |
| differently, depending on the protocol that the path is using. To allow |
| this, add a protocol subsection to the overrides section in |
| multipath.conf, which allows select path-specific options to be set. |
| This commit simply adds the subsection, and handles merging matching |
| entries. Future patches will make use of the section. |
| |
| Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com> |
| Reviewed-by: Martin Wilck <mwilck@suse.com> |
| |
| libmultipath/config.c | 83 ++++++++++++++++++++++++++++++++++++ |
| libmultipath/config.h | 10 +++++ |
| libmultipath/dict.c | 99 +++++++++++++++++++++++++++++++++++++++++++ |
| libmultipath/print.c | 44 +++++++++++++++++++ |
| 4 files changed, 236 insertions(+) |
| |
| diff --git a/libmultipath/config.c b/libmultipath/config.c |
| index aa79561e..88975323 100644 |
| |
| |
| @@ -173,6 +173,18 @@ char *get_mpe_wwid(vector mptable, char *alias) |
| return NULL; |
| } |
| |
| +static void |
| +free_pctable (vector pctable) |
| +{ |
| + int i; |
| + struct pcentry *pce; |
| + |
| + vector_foreach_slot(pctable, pce, i) |
| + free(pce); |
| + |
| + vector_free(pctable); |
| +} |
| + |
| void |
| free_hwe (struct hwentry * hwe) |
| { |
| @@ -218,6 +230,9 @@ free_hwe (struct hwentry * hwe) |
| if (hwe->bl_product) |
| FREE(hwe->bl_product); |
| |
| + if (hwe->pctable) |
| + free_pctable(hwe->pctable); |
| + |
| FREE(hwe); |
| } |
| |
| @@ -299,6 +314,15 @@ alloc_hwe (void) |
| return hwe; |
| } |
| |
| +struct pcentry * |
| +alloc_pce (void) |
| +{ |
| + struct pcentry *pce = (struct pcentry *) |
| + calloc(1, sizeof(struct pcentry)); |
| + pce->type = -1; |
| + return pce; |
| +} |
| + |
| static char * |
| set_param_str(const char * str) |
| { |
| @@ -332,6 +356,13 @@ set_param_str(const char * str) |
| if (!dst->s && src->s) \ |
| dst->s = src->s |
| |
| +static void |
| +merge_pce(struct pcentry *dst, struct pcentry *src) |
| +{ |
| + merge_num(fast_io_fail); |
| + merge_num(dev_loss); |
| + merge_num(eh_deadline); |
| +} |
| |
| static void |
| merge_hwe (struct hwentry * dst, struct hwentry * src) |
| @@ -538,6 +569,51 @@ out: |
| return 1; |
| } |
| |
| +static void |
| +validate_pctable(struct hwentry *ovr, int idx, const char *table_desc) |
| +{ |
| + struct pcentry *pce; |
| + |
| + if (!ovr || !ovr->pctable) |
| + return; |
| + |
| + vector_foreach_slot_after(ovr->pctable, pce, idx) { |
| + if (pce->type < 0) { |
| + condlog(0, "protocol section in %s missing type", |
| + table_desc); |
| + vector_del_slot(ovr->pctable, idx--); |
| + free(pce); |
| + } |
| + } |
| + |
| + if (VECTOR_SIZE(ovr->pctable) == 0) { |
| + vector_free(ovr->pctable); |
| + ovr->pctable = NULL; |
| + } |
| +} |
| + |
| +static void |
| +merge_pctable(struct hwentry *ovr) |
| +{ |
| + struct pcentry *pce1, *pce2; |
| + int i, j; |
| + |
| + if (!ovr || !ovr->pctable) |
| + return; |
| + |
| + vector_foreach_slot(ovr->pctable, pce1, i) { |
| + j = i + 1; |
| + vector_foreach_slot_after(ovr->pctable, pce2, j) { |
| + if (pce1->type != pce2->type) |
| + continue; |
| + merge_pce(pce2,pce1); |
| + vector_del_slot(ovr->pctable, i--); |
| + free(pce1); |
| + break; |
| + } |
| + } |
| +} |
| + |
| static void |
| factorize_hwtable (vector hw, int n, const char *table_desc) |
| { |
| @@ -666,6 +742,7 @@ process_config_dir(struct config *conf, char *dir) |
| int i, n; |
| char path[LINE_MAX]; |
| int old_hwtable_size; |
| + int old_pctable_size = 0; |
| |
| if (dir[0] != '/') { |
| condlog(1, "config_dir '%s' must be a fully qualified path", |
| @@ -692,11 +769,15 @@ process_config_dir(struct config *conf, char *dir) |
| continue; |
| |
| old_hwtable_size = VECTOR_SIZE(conf->hwtable); |
| + old_pctable_size = conf->overrides ? |
| + VECTOR_SIZE(conf->overrides->pctable) : 0; |
| snprintf(path, LINE_MAX, "%s/%s", dir, namelist[i]->d_name); |
| path[LINE_MAX-1] = '\0'; |
| process_file(conf, path); |
| factorize_hwtable(conf->hwtable, old_hwtable_size, |
| namelist[i]->d_name); |
| + validate_pctable(conf->overrides, old_pctable_size, |
| + namelist[i]->d_name); |
| } |
| pthread_cleanup_pop(1); |
| } |
| @@ -784,6 +865,7 @@ load_config (char * file) |
| goto out; |
| } |
| factorize_hwtable(conf->hwtable, builtin_hwtable_size, file); |
| + validate_pctable(conf->overrides, 0, file); |
| } else { |
| condlog(0, "/etc/multipath.conf does not exist, blacklisting all devices."); |
| condlog(0, "You can run \"/sbin/mpathconf --enable\" to create"); |
| @@ -898,6 +980,7 @@ load_config (char * file) |
| goto out; |
| } |
| |
| + merge_pctable(conf->overrides); |
| merge_mptable(conf->mptable); |
| merge_blacklist(conf->blist_devnode); |
| merge_blacklist(conf->blist_property); |
| diff --git a/libmultipath/config.h b/libmultipath/config.h |
| index e2e3f143..143116b3 100644 |
| |
| |
| @@ -41,6 +41,13 @@ enum force_reload_types { |
| FORCE_RELOAD_WEAK, |
| }; |
| |
| +struct pcentry { |
| + int type; |
| + int fast_io_fail; |
| + unsigned int dev_loss; |
| + int eh_deadline; |
| +}; |
| + |
| struct hwentry { |
| char * vendor; |
| char * product; |
| @@ -86,6 +93,8 @@ struct hwentry { |
| int vpd_vendor_id; |
| int recheck_wwid; |
| char * bl_product; |
| + |
| + vector pctable; |
| }; |
| |
| struct mpentry { |
| @@ -240,6 +249,7 @@ char * get_mpe_wwid (vector mptable, char * alias); |
| |
| struct hwentry * alloc_hwe (void); |
| struct mpentry * alloc_mpe (void); |
| +struct pcentry * alloc_pce (void); |
| |
| void free_hwe (struct hwentry * hwe); |
| void free_hwtable (vector hwtable); |
| diff --git a/libmultipath/dict.c b/libmultipath/dict.c |
| index 5a0255b0..8321ec1e 100644 |
| |
| |
| @@ -457,6 +457,29 @@ snprint_mp_ ## option (struct config *conf, char * buff, int len, \ |
| return function (buff, len, mpe->option); \ |
| } |
| |
| +#define declare_pc_handler(option, function) \ |
| +static int \ |
| +pc_ ## option ## _handler (struct config *conf, vector strvec, \ |
| + const char *file, int line_nr) \ |
| +{ \ |
| + struct pcentry *pce; \ |
| + if (!conf->overrides || !conf->overrides->pctable) \ |
| + return 1; \ |
| + pce = VECTOR_LAST_SLOT(conf->overrides->pctable); \ |
| + if (!pce) \ |
| + return 1; \ |
| + return function (strvec, &pce->option, file, line_nr); \ |
| +} |
| + |
| +#define declare_pc_snprint(option, function) \ |
| +static int \ |
| +snprint_pc_ ## option (struct config *conf, char * buff, int len, \ |
| + const void *data) \ |
| +{ \ |
| + const struct pcentry *pce = (const struct pcentry *)data; \ |
| + return function(buff, len, pce->option); \ |
| +} |
| + |
| static int checkint_handler(struct config *conf, vector strvec, |
| const char *file, int line_nr) |
| { |
| @@ -1022,6 +1045,8 @@ declare_ovr_handler(fast_io_fail, set_undef_off_zero) |
| declare_ovr_snprint(fast_io_fail, print_undef_off_zero) |
| declare_hw_handler(fast_io_fail, set_undef_off_zero) |
| declare_hw_snprint(fast_io_fail, print_undef_off_zero) |
| +declare_pc_handler(fast_io_fail, set_undef_off_zero) |
| +declare_pc_snprint(fast_io_fail, print_undef_off_zero) |
| |
| static int |
| set_dev_loss(vector strvec, void *ptr, const char *file, int line_nr) |
| @@ -1059,6 +1084,8 @@ declare_ovr_handler(dev_loss, set_dev_loss) |
| declare_ovr_snprint(dev_loss, print_dev_loss) |
| declare_hw_handler(dev_loss, set_dev_loss) |
| declare_hw_snprint(dev_loss, print_dev_loss) |
| +declare_pc_handler(dev_loss, set_dev_loss) |
| +declare_pc_snprint(dev_loss, print_dev_loss) |
| |
| declare_def_handler(eh_deadline, set_undef_off_zero) |
| declare_def_snprint(eh_deadline, print_undef_off_zero) |
| @@ -1066,6 +1093,8 @@ declare_ovr_handler(eh_deadline, set_undef_off_zero) |
| declare_ovr_snprint(eh_deadline, print_undef_off_zero) |
| declare_hw_handler(eh_deadline, set_undef_off_zero) |
| declare_hw_snprint(eh_deadline, print_undef_off_zero) |
| +declare_pc_handler(eh_deadline, set_undef_off_zero) |
| +declare_pc_snprint(eh_deadline, print_undef_off_zero) |
| |
| static int |
| set_pgpolicy(vector strvec, void *ptr, const char *file, int line_nr) |
| @@ -1876,6 +1905,69 @@ declare_mp_snprint(wwid, print_str) |
| declare_mp_handler(alias, set_str_noslash) |
| declare_mp_snprint(alias, print_str) |
| |
| + |
| +static int |
| +protocol_handler(struct config *conf, vector strvec, const char *file, |
| + int line_nr) |
| +{ |
| + struct pcentry *pce; |
| + |
| + if (!conf->overrides) |
| + return 1; |
| + |
| + if (!conf->overrides->pctable && |
| + !(conf->overrides->pctable = vector_alloc())) |
| + return 1; |
| + |
| + if (!(pce = alloc_pce())) |
| + return 1; |
| + |
| + if (!vector_alloc_slot(conf->overrides->pctable)) { |
| + free(pce); |
| + return 1; |
| + } |
| + vector_set_slot(conf->overrides->pctable, pce); |
| + |
| + return 0; |
| +} |
| + |
| +static int |
| +set_protocol_type(vector strvec, void *ptr, const char *file, int line_nr) |
| +{ |
| + int *int_ptr = (int *)ptr; |
| + char *buff; |
| + int i; |
| + |
| + buff = set_value(strvec); |
| + |
| + if (!buff) |
| + return 1; |
| + |
| + for (i = 0; i <= LAST_BUS_PROTOCOL_ID; i++) { |
| + if (protocol_name[i] && !strcmp(buff, protocol_name[i])) { |
| + *int_ptr = i; |
| + break; |
| + } |
| + } |
| + if (i > LAST_BUS_PROTOCOL_ID) |
| + condlog(1, "%s line %d, invalid value for type: \"%s\"", |
| + file, line_nr, buff); |
| + |
| + free(buff); |
| + return 0; |
| +} |
| + |
| +static int |
| +print_protocol_type(char *buff, int len, int type) |
| +{ |
| + if (type < 0) |
| + return 0; |
| + return snprintf(buff, len, "\"%s\"", protocol_name[type]); |
| +} |
| + |
| +declare_pc_handler(type, set_protocol_type) |
| +declare_pc_snprint(type, print_protocol_type) |
| + |
| /* |
| * deprecated handlers |
| */ |
| @@ -2117,6 +2209,13 @@ init_keywords(vector keywords) |
| install_keyword("ghost_delay", &ovr_ghost_delay_handler, &snprint_ovr_ghost_delay); |
| install_keyword("all_tg_pt", &ovr_all_tg_pt_handler, &snprint_ovr_all_tg_pt); |
| install_keyword("recheck_wwid", &ovr_recheck_wwid_handler, &snprint_ovr_recheck_wwid); |
| + install_keyword_multi("protocol", &protocol_handler, NULL); |
| + install_sublevel(); |
| + install_keyword("type", &pc_type_handler, &snprint_pc_type); |
| + install_keyword("fast_io_fail_tmo", &pc_fast_io_fail_handler, &snprint_pc_fast_io_fail); |
| + install_keyword("dev_loss_tmo", &pc_dev_loss_handler, &snprint_pc_dev_loss); |
| + install_keyword("eh_deadline", &pc_eh_deadline_handler, &snprint_pc_eh_deadline); |
| + install_sublevel_end(); |
| |
| install_keyword_root("multipaths", &multipaths_handler); |
| install_keyword_multi("multipath", &multipath_handler, NULL); |
| diff --git a/libmultipath/print.c b/libmultipath/print.c |
| index 1f6d27bd..8a6fbe83 100644 |
| |
| |
| @@ -1392,6 +1392,39 @@ snprint_multipath_topology_json (char * buff, int len, const struct vectors * ve |
| return fwd; |
| } |
| |
| +static int |
| +snprint_pcentry (const struct config *conf, char *buff, int len, |
| + const struct pcentry *pce) |
| +{ |
| + int i; |
| + int fwd = 0; |
| + struct keyword *kw; |
| + struct keyword *rootkw; |
| + |
| + rootkw = find_keyword(conf->keywords, NULL, "overrides"); |
| + if (!rootkw || !rootkw->sub) |
| + return 0; |
| + |
| + rootkw = find_keyword(conf->keywords, rootkw->sub, "protocol"); |
| + if (!rootkw) |
| + return 0; |
| + |
| + fwd += snprintf(buff + fwd, len - fwd, "\tprotocol {\n"); |
| + if (fwd >= len) |
| + return len; |
| + |
| + iterate_sub_keywords(rootkw, kw, i) { |
| + fwd += snprint_keyword(buff + fwd, len - fwd, "\t\t%k %v\n", |
| + kw, pce); |
| + if (fwd >= len) |
| + return len; |
| + } |
| + fwd += snprintf(buff + fwd, len - fwd, "\t}\n"); |
| + if (fwd >= len) |
| + return len; |
| + return fwd; |
| +} |
| + |
| static int |
| snprint_hwentry (const struct config *conf, |
| char * buff, int len, const struct hwentry * hwe) |
| @@ -1575,6 +1608,17 @@ static int snprint_overrides(const struct config *conf, char * buff, int len, |
| if (fwd >= len) |
| return len; |
| } |
| + |
| + if (overrides->pctable) { |
| + struct pcentry *pce; |
| + |
| + vector_foreach_slot(overrides->pctable, pce, i) { |
| + fwd += snprint_pcentry(conf, buff + fwd, len - fwd, |
| + pce); |
| + if (fwd >= len) |
| + return len; |
| + } |
| + } |
| out: |
| fwd += snprintf(buff + fwd, len - fwd, "}\n"); |
| if (fwd >= len) |