| @@ -39,6 +39,7 @@ · 38 unmodified lines |
| 39 | 39 | #include <linux/rcupdate.h> |
| 40 | 40 | #include <linux/ipmi.h> |
| 41 | 41 | #include <linux/ipmi_smi.h> |
| 42 | +#include <linux/workqueue.h> |
| 42 | 43 | #include "ipmi_si.h" |
| 43 | 44 | #include "ipmi_si_sm.h" |
| 44 | 45 | #include <linux/string.h> |
| @@ -252,6 +253,8 @@ struct smi_info { · 207 unmodified lines |
| 252 | 253 | |
| 253 | 254 | struct task_struct *thread; |
| 254 | 255 | |
| 256 | + struct work_struct init_work; |
| 257 | + |
| 255 | 258 | struct list_head link; |
| 256 | 259 | }; |
| 257 | 260 | |
| @@ -272,6 +275,7 @@ static bool unload_when_empty = true; · 14 unmodified lines |
| 272 | 275 | static int try_smi_init(struct smi_info *smi); |
| 273 | 276 | static void cleanup_one_si(struct smi_info *smi_info); |
| 274 | 277 | static void cleanup_ipmi_si(void); |
| 278 | +static void smi_init_work_fn(struct work_struct *work); |
| 275 | 279 | |
| 276 | 280 | #ifdef DEBUG_TIMING |
| 277 | 281 | void debug_timestamp(struct smi_info *smi_info, char *msg) |
| @@ -1970,6 +1974,7 @@ int ipmi_si_add_smi(struct si_sm_io *io) · 1692 unmodified lines |
| 1970 | 1974 | if (!new_smi) |
| 1971 | 1975 | return -ENOMEM; |
| 1972 | 1976 | spin_lock_init(&new_smi->si_lock); |
| 1977 | + INIT_WORK(&new_smi->init_work, smi_init_work_fn); |
| 1973 | 1978 | |
| 1974 | 1979 | new_smi->io = *io; |
| 1975 | 1980 | |
| @@ -1982,7 +1987,12 @@ int ipmi_si_add_smi(struct si_sm_io *io) · 6 unmodified lines |
| 1982 | 1987 | dev_info(dup->io.dev, |
| 1983 | 1988 | "Removing SMBIOS-specified %s state machine in favor of ACPI\n", |
| 1984 | 1989 | si_to_str[new_smi->io.si_info->type]); |
| 1990 | + list_del(&dup->link); |
| 1991 | + mutex_unlock(&smi_infos_lock); |
| 1992 | + |
| 1985 | 1993 | cleanup_one_si(dup); |
| 1994 | + |
| 1995 | + mutex_lock(&smi_infos_lock); |
| 1986 | 1996 | } else { |
| 1987 | 1997 | dev_info(new_smi->io.dev, |
| 1988 | 1998 | "%s-specified %s state machine: duplicate\n", |
| @@ -2000,8 +2010,12 @@ int ipmi_si_add_smi(struct si_sm_io *io) · 11 unmodified lines |
| 2000 | 2010 | |
| 2001 | 2011 | list_add_tail(&new_smi->link, &smi_infos); |
| 2002 | 2012 | |
| 2003 | | − if (initialized) |
| 2004 | | − rv = try_smi_init(new_smi); |
| 2013 | + if (initialized) { |
| 2014 | + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) |
| 2015 | + queue_work(system_dfl_wq, &new_smi->init_work); |
| 2016 | + else |
| 2017 | + rv = try_smi_init(new_smi); |
| 2018 | + } |
| 2005 | 2019 | out_err: |
| 2006 | 2020 | mutex_unlock(&smi_infos_lock); |
| 2007 | 2021 | return rv; |
| @@ -2174,6 +2188,15 @@ static bool __init ipmi_smi_info_same(struct smi_info *e1, struct smi_info *e2) · 166 unmodified lines |
| 2174 | 2188 | e1->io.addr_data == e2->io.addr_data); |
| 2175 | 2189 | } |
| 2176 | 2190 | |
| 2191 | +static void smi_init_work_fn(struct work_struct *work) |
| 2192 | +{ |
| 2193 | + struct smi_info *smi = container_of(work, struct smi_info, init_work); |
| 2194 | + |
| 2195 | + mutex_lock(&smi_infos_lock); |
| 2196 | + try_smi_init(smi); |
| 2197 | + mutex_unlock(&smi_infos_lock); |
| 2198 | +} |
| 2199 | + |
| 2177 | 2200 | static int __init init_ipmi_si(void) |
| 2178 | 2201 | { |
| 2179 | 2202 | struct smi_info *e, *e2; |
| @@ -2219,8 +2242,12 @@ static int __init init_ipmi_si(void) · 39 unmodified lines |
| 2219 | 2242 | break; |
| 2220 | 2243 | } |
| 2221 | 2244 | } |
| 2222 | | − if (!dup) |
| 2223 | | − try_smi_init(e); |
| 2245 | + if (!dup) { |
| 2246 | + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) |
| 2247 | + queue_work(system_unbound_wq, &e->init_work); |
| 2248 | + else |
| 2249 | + try_smi_init(e); |
| 2250 | + } |
| 2224 | 2251 | } |
| 2225 | 2252 | |
| 2226 | 2253 | /* |
| @@ -2253,8 +2280,12 @@ static int __init init_ipmi_si(void) · 26 unmodified lines |
| 2253 | 2280 | break; |
| 2254 | 2281 | } |
| 2255 | 2282 | } |
| 2256 | | − if (!dup) |
| 2257 | | − try_smi_init(e); |
| 2283 | + if (!dup) { |
| 2284 | + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) |
| 2285 | + queue_work(system_unbound_wq, &e->init_work); |
| 2286 | + else |
| 2287 | + try_smi_init(e); |
| 2288 | + } |
| 2258 | 2289 | } |
| 2259 | 2290 | |
| 2260 | 2291 | initialized = true; |
| @@ -2344,31 +2375,36 @@ static void shutdown_smi(void *send_info) · 83 unmodified lines |
| 2344 | 2375 | } |
| 2345 | 2376 | |
| 2346 | 2377 | /* |
| 2347 | | − * Must be called with smi_infos_lock held, to serialize the |
| 2348 | | − * smi_info->intf check. |
| 2378 | + * Must be called with smi_info unlinked from smi_infos and smi_infos_lock released. |
| 2349 | 2379 | */ |
| 2350 | 2380 | static void cleanup_one_si(struct smi_info *smi_info) |
| 2351 | 2381 | { |
| 2352 | 2382 | if (!smi_info) |
| 2353 | 2383 | return; |
| 2354 | 2384 | |
| 2355 | | − list_del(&smi_info->link); |
| 2385 | + if (IS_ENABLED(CONFIG_IPMI_SI_ASYNC_INIT)) |
| 2386 | + cancel_work_sync(&smi_info->init_work); |
| 2387 | + |
| 2356 | 2388 | ipmi_unregister_smi(smi_info->intf); |
| 2357 | 2389 | kfree(smi_info); |
| 2358 | 2390 | } |
| 2359 | 2391 | |
| 2360 | 2392 | void ipmi_si_remove_by_dev(struct device *dev) |
| 2361 | 2393 | { |
| 2362 | | − struct smi_info *e; |
| 2394 | + struct smi_info *e = NULL, *tmp; |
| 2363 | 2395 | |
| 2364 | 2396 | mutex_lock(&smi_infos_lock); |
| 2365 | | − list_for_each_entry(e, &smi_infos, link) { |
| 2366 | | − if (e->io.dev == dev) { |
| 2367 | | − cleanup_one_si(e); |
| 2397 | + list_for_each_entry(tmp, &smi_infos, link) { |
| 2398 | + if (tmp->io.dev == dev) { |
| 2399 | + e = tmp; |
| 2400 | + list_del(&e->link); |
| 2368 | 2401 | break; |
| 2369 | 2402 | } |
| 2370 | 2403 | } |
| 2371 | 2404 | mutex_unlock(&smi_infos_lock); |
| 2405 | + |
| 2406 | + if (e) |
| 2407 | + cleanup_one_si(e); |
| 2372 | 2408 | } |
| 2373 | 2409 | |
| 2374 | 2410 | struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, |
| @@ -2377,6 +2413,7 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, · 2 unmodified lines |
| 2377 | 2413 | /* remove */ |
| 2378 | 2414 | struct smi_info *e, *tmp_e; |
| 2379 | 2415 | struct device *dev = NULL; |
| 2416 | + LIST_HEAD(to_clean); |
| 2380 | 2417 | |
| 2381 | 2418 | mutex_lock(&smi_infos_lock); |
| 2382 | 2419 | list_for_each_entry_safe(e, tmp_e, &smi_infos, link) { |
| @@ -2386,17 +2423,23 @@ struct device *ipmi_si_remove_by_data(int addr_space, enum si_type si_type, · 3 unmodified lines |
| 2386 | 2423 | continue; |
| 2387 | 2424 | if (e->io.addr_data == addr) { |
| 2388 | 2425 | dev = get_device(e->io.dev); |
| 2389 | | − cleanup_one_si(e); |
| 2426 | + list_move_tail(&e->link, &to_clean); |
| 2390 | 2427 | } |
| 2391 | 2428 | } |
| 2392 | 2429 | mutex_unlock(&smi_infos_lock); |
| 2393 | 2430 | |
| 2431 | + list_for_each_entry_safe(e, tmp_e, &to_clean, link) { |
| 2432 | + list_del(&e->link); |
| 2433 | + cleanup_one_si(e); |
| 2434 | + } |
| 2435 | + |
| 2394 | 2436 | return dev; |
| 2395 | 2437 | } |
| 2396 | 2438 | |
| 2397 | 2439 | static void cleanup_ipmi_si(void) |
| 2398 | 2440 | { |
| 2399 | 2441 | struct smi_info *e, *tmp_e; |
| 2442 | + LIST_HEAD(to_clean); |
| 2400 | 2443 | |
| 2401 | 2444 | if (!initialized) |
| 2402 | 2445 | return; |
| @@ -2410,10 +2453,14 @@ static void cleanup_ipmi_si(void) · 7 unmodified lines |
| 2410 | 2453 | ipmi_si_platform_shutdown(); |
| 2411 | 2454 | |
| 2412 | 2455 | mutex_lock(&smi_infos_lock); |
| 2413 | | − list_for_each_entry_safe(e, tmp_e, &smi_infos, link) |
| 2414 | | − cleanup_one_si(e); |
| 2456 | + list_splice_init(&smi_infos, &to_clean); |
| 2415 | 2457 | mutex_unlock(&smi_infos_lock); |
| 2416 | 2458 | |
| 2459 | + list_for_each_entry_safe(e, tmp_e, &to_clean, link) { |
| 2460 | + list_del(&e->link); |
| 2461 | + cleanup_one_si(e); |
| 2462 | + } |
| 2463 | + |
| 2417 | 2464 | ipmi_si_hardcode_exit(); |
| 2418 | 2465 | ipmi_si_hotmod_exit(); |
| 2419 | 2466 | } |