Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

driver core: fw_devlink: Allow marking a fwnode link as being part of a cycle

To improve detection and handling of dependency cycles, we need to be
able to mark fwnode links as being part of cycles. fwnode links marked
as being part of a cycle should not block their consumers from probing.

Fixes: 2de9d8e0d2fe ("driver core: fw_devlink: Improve handling of cyclic dependencies")
Signed-off-by: Saravana Kannan <saravanak@google.com>
Tested-by: Colin Foster <colin.foster@in-advantage.com>
Tested-by: Sudeep Holla <sudeep.holla@arm.com>
Tested-by: Douglas Anderson <dianders@chromium.org>
Tested-by: Geert Uytterhoeven <geert+renesas@glider.be>
Tested-by: Luca Weiss <luca.weiss@fairphone.com> # qcom/sm7225-fairphone-fp4
Link: https://lore.kernel.org/r/20230207014207.1678715-7-saravanak@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Saravana Kannan and committed by
Greg Kroah-Hartman
6a6dfdf8 67cad5c6

+50 -11
+40 -10
drivers/base/core.c
··· 76 76 * are ignored and there is no reference counting. 77 77 */ 78 78 static int __fwnode_link_add(struct fwnode_handle *con, 79 - struct fwnode_handle *sup) 79 + struct fwnode_handle *sup, u8 flags) 80 80 { 81 81 struct fwnode_link *link; 82 82 83 83 list_for_each_entry(link, &sup->consumers, s_hook) 84 - if (link->consumer == con) 84 + if (link->consumer == con) { 85 + link->flags |= flags; 85 86 return 0; 87 + } 86 88 87 89 link = kzalloc(sizeof(*link), GFP_KERNEL); 88 90 if (!link) ··· 94 92 INIT_LIST_HEAD(&link->s_hook); 95 93 link->consumer = con; 96 94 INIT_LIST_HEAD(&link->c_hook); 95 + link->flags = flags; 97 96 98 97 list_add(&link->s_hook, &sup->consumers); 99 98 list_add(&link->c_hook, &con->suppliers); ··· 109 106 int ret; 110 107 111 108 mutex_lock(&fwnode_link_lock); 112 - ret = __fwnode_link_add(con, sup); 109 + ret = __fwnode_link_add(con, sup, 0); 113 110 mutex_unlock(&fwnode_link_lock); 114 111 return ret; 115 112 } ··· 127 124 list_del(&link->s_hook); 128 125 list_del(&link->c_hook); 129 126 kfree(link); 127 + } 128 + 129 + /** 130 + * __fwnode_link_cycle - Mark a fwnode link as being part of a cycle. 131 + * @link: the fwnode_link to be marked 132 + * 133 + * The fwnode_link_lock needs to be held when this function is called. 134 + */ 135 + static void __fwnode_link_cycle(struct fwnode_link *link) 136 + { 137 + pr_debug("%pfwf: Relaxing link with %pfwf\n", 138 + link->consumer, link->supplier); 139 + link->flags |= FWLINK_FLAG_CYCLE; 130 140 } 131 141 132 142 /** ··· 215 199 struct fwnode_link *link, *tmp; 216 200 217 201 list_for_each_entry_safe(link, tmp, &from->consumers, s_hook) { 218 - __fwnode_link_add(link->consumer, to); 202 + __fwnode_link_add(link->consumer, to, link->flags); 219 203 __fwnode_link_del(link); 220 204 } 221 205 } ··· 1057 1041 (dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT)); 1058 1042 } 1059 1043 1044 + static struct fwnode_handle *fwnode_links_check_suppliers( 1045 + struct fwnode_handle *fwnode) 1046 + { 1047 + struct fwnode_link *link; 1048 + 1049 + if (!fwnode || fw_devlink_is_permissive()) 1050 + return NULL; 1051 + 1052 + list_for_each_entry(link, &fwnode->suppliers, c_hook) 1053 + if (!(link->flags & FWLINK_FLAG_CYCLE)) 1054 + return link->supplier; 1055 + 1056 + return NULL; 1057 + } 1058 + 1060 1059 /** 1061 1060 * device_links_check_suppliers - Check presence of supplier drivers. 1062 1061 * @dev: Consumer device. ··· 1099 1068 * probe. 1100 1069 */ 1101 1070 mutex_lock(&fwnode_link_lock); 1102 - if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) && 1103 - !fw_devlink_is_permissive()) { 1104 - sup_fw = list_first_entry(&dev->fwnode->suppliers, 1105 - struct fwnode_link, 1106 - c_hook)->supplier; 1071 + sup_fw = fwnode_links_check_suppliers(dev->fwnode); 1072 + if (sup_fw) { 1107 1073 if (!dev_is_best_effort(dev)) { 1108 1074 fwnode_ret = -EPROBE_DEFER; 1109 1075 dev_err_probe(dev, -EPROBE_DEFER, ··· 1289 1261 bool val; 1290 1262 1291 1263 device_lock(dev); 1292 - val = !list_empty(&dev->fwnode->suppliers); 1264 + mutex_lock(&fwnode_link_lock); 1265 + val = !!fwnode_links_check_suppliers(dev->fwnode); 1266 + mutex_unlock(&fwnode_link_lock); 1293 1267 device_unlock(dev); 1294 1268 return sysfs_emit(buf, "%u\n", val); 1295 1269 }
+10 -1
include/linux/fwnode.h
··· 18 18 struct device; 19 19 20 20 /* 21 - * fwnode link flags 21 + * fwnode flags 22 22 * 23 23 * LINKS_ADDED: The fwnode has already be parsed to add fwnode links. 24 24 * NOT_DEVICE: The fwnode will never be populated as a struct device. ··· 36 36 #define FWNODE_FLAG_INITIALIZED BIT(2) 37 37 #define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3) 38 38 #define FWNODE_FLAG_BEST_EFFORT BIT(4) 39 + #define FWNODE_FLAG_VISITED BIT(5) 39 40 40 41 struct fwnode_handle { 41 42 struct fwnode_handle *secondary; ··· 47 46 u8 flags; 48 47 }; 49 48 49 + /* 50 + * fwnode link flags 51 + * 52 + * CYCLE: The fwnode link is part of a cycle. Don't defer probe. 53 + */ 54 + #define FWLINK_FLAG_CYCLE BIT(0) 55 + 50 56 struct fwnode_link { 51 57 struct fwnode_handle *supplier; 52 58 struct list_head s_hook; 53 59 struct fwnode_handle *consumer; 54 60 struct list_head c_hook; 61 + u8 flags; 55 62 }; 56 63 57 64 /**