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

EDAC/mc: Change mci device removal to use put_device()

There are dimm and csrow devices linked to the mci device esp. to show
up in sysfs. It must be granted that children devices are removed before
its mci parent. Thus, the release functions must be called in the
correct order and may not miss any child before releasing its parent. In
the current implementation this is only granted by the correct order of
release functions.

A much better approach is to use put_device() that releases the device
only after all users are gone. It is the recommended way to release a
device and free its memory. The function uses the device's refcount and
only frees it if there are no users of it anymore such as children.

So implement a mci_release() function to remove mci devices, use
put_device() to free them and early initialize the mci device right
after its struct has been allocated.

Change the release function so that it can be universally used no
matter if the device is registered or not. Since subsequent dimm
and csrow sysfs links are implemented as children devices, their
refcounts will keep the parent mci device from being removed as long
as sysfs entries exist and until all users have been unregistered in
edac_remove_sysfs_mci_device().

Remove edac_unregister_sysfs() and merge mci sysfs removal into
edac_remove_sysfs_mci_device(). There is only a single instance now that
removes the sysfs entries. The function can now be used in the error
paths for cleanup.

Also, create device release functions for all involved devices
(dev->release), remove device_type release functions (dev_type->
release) and also use dev->init_name instead of dev_set_name().

[ bp: Massage commit message and comments. ]

Signed-off-by: Robert Richter <rrichter@marvell.com>
Signed-off-by: Borislav Petkov <bp@suse.de>
Acked-by: Aristeu Rozanski <aris@redhat.com>
Link: https://lkml.kernel.org/r/20200212120340.4764-5-rrichter@marvell.com

authored by

Robert Richter and committed by
Borislav Petkov
bea1bfd5 11a48a5a

+47 -56
+9 -3
drivers/edac/edac_mc.c
··· 278 278 279 279 static void _edac_mc_free(struct mem_ctl_info *mci) 280 280 { 281 + put_device(&mci->dev); 282 + } 283 + 284 + static void mci_release(struct device *dev) 285 + { 286 + struct mem_ctl_info *mci = container_of(dev, struct mem_ctl_info, dev); 281 287 struct csrow_info *csr; 282 288 int i, chn, row; 283 289 ··· 376 370 mci = kzalloc(size, GFP_KERNEL); 377 371 if (mci == NULL) 378 372 return NULL; 373 + 374 + mci->dev.release = mci_release; 375 + device_initialize(&mci->dev); 379 376 380 377 /* Adjust pointers so they point within the memory we just allocated 381 378 * rather than an imaginary chunk of memory located at address 0. ··· 513 504 void edac_mc_free(struct mem_ctl_info *mci) 514 505 { 515 506 edac_dbg(1, "\n"); 516 - 517 - if (device_is_registered(&mci->dev)) 518 - edac_unregister_sysfs(mci); 519 507 520 508 _edac_mc_free(mci); 521 509 }
+38 -52
drivers/edac/edac_mc_sysfs.c
··· 274 274 NULL 275 275 }; 276 276 277 - static void csrow_attr_release(struct device *dev) 278 - { 279 - /* release device with _edac_mc_free() */ 280 - } 281 - 282 277 static const struct device_type csrow_attr_type = { 283 278 .groups = csrow_attr_groups, 284 - .release = csrow_attr_release, 285 279 }; 286 280 287 281 /* ··· 381 387 NULL 382 388 }; 383 389 390 + static void csrow_release(struct device *dev) 391 + { 392 + /* 393 + * Nothing to do, just unregister sysfs here. The mci 394 + * device owns the data and will also release it. 395 + */ 396 + } 397 + 384 398 static inline int nr_pages_per_csrow(struct csrow_info *csrow) 385 399 { 386 400 int chan, nr_pages = 0; ··· 407 405 408 406 csrow->dev.type = &csrow_attr_type; 409 407 csrow->dev.groups = csrow_dev_groups; 408 + csrow->dev.release = csrow_release; 410 409 device_initialize(&csrow->dev); 411 410 csrow->dev.parent = &mci->dev; 412 411 csrow->mci = mci; ··· 444 441 445 442 error: 446 443 for (--i; i >= 0; i--) { 447 - csrow = mci->csrows[i]; 448 - if (!nr_pages_per_csrow(csrow)) 449 - continue; 450 - device_unregister(&mci->csrows[i]->dev); 444 + if (device_is_registered(&mci->csrows[i]->dev)) 445 + device_unregister(&mci->csrows[i]->dev); 451 446 } 452 447 453 448 return err; ··· 454 453 static void edac_delete_csrow_objects(struct mem_ctl_info *mci) 455 454 { 456 455 int i; 457 - struct csrow_info *csrow; 458 456 459 - for (i = mci->nr_csrows - 1; i >= 0; i--) { 460 - csrow = mci->csrows[i]; 461 - if (!nr_pages_per_csrow(csrow)) 462 - continue; 463 - device_unregister(&mci->csrows[i]->dev); 457 + for (i = 0; i < mci->nr_csrows; i++) { 458 + if (device_is_registered(&mci->csrows[i]->dev)) 459 + device_unregister(&mci->csrows[i]->dev); 464 460 } 465 461 } 462 + 466 463 #endif 467 464 468 465 /* ··· 601 602 NULL 602 603 }; 603 604 604 - static void dimm_attr_release(struct device *dev) 605 - { 606 - /* release device with _edac_mc_free() */ 607 - } 608 - 609 605 static const struct device_type dimm_attr_type = { 610 606 .groups = dimm_attr_groups, 611 - .release = dimm_attr_release, 612 607 }; 608 + 609 + static void dimm_release(struct device *dev) 610 + { 611 + /* 612 + * Nothing to do, just unregister sysfs here. The mci 613 + * device owns the data and will also release it. 614 + */ 615 + } 613 616 614 617 /* Create a DIMM object under specifed memory controller device */ 615 618 static int edac_create_dimm_object(struct mem_ctl_info *mci, ··· 621 620 dimm->mci = mci; 622 621 623 622 dimm->dev.type = &dimm_attr_type; 623 + dimm->dev.release = dimm_release; 624 624 device_initialize(&dimm->dev); 625 625 626 626 dimm->dev.parent = &mci->dev; ··· 886 884 NULL 887 885 }; 888 886 889 - static void mci_attr_release(struct device *dev) 890 - { 891 - /* release device with _edac_mc_free() */ 892 - } 893 - 894 887 static const struct device_type mci_attr_type = { 895 888 .groups = mci_attr_groups, 896 - .release = mci_attr_release, 897 889 }; 898 890 899 891 /* ··· 906 910 907 911 /* get the /sys/devices/system/edac subsys reference */ 908 912 mci->dev.type = &mci_attr_type; 909 - device_initialize(&mci->dev); 910 - 911 913 mci->dev.parent = mci_pdev; 912 914 mci->dev.groups = groups; 913 915 dev_set_name(&mci->dev, "mc%d", mci->mc_idx); ··· 915 921 err = device_add(&mci->dev); 916 922 if (err < 0) { 917 923 edac_dbg(1, "failure: create device %s\n", dev_name(&mci->dev)); 918 - put_device(&mci->dev); 924 + /* no put_device() here, free mci with _edac_mc_free() */ 919 925 return err; 920 926 } 921 927 ··· 931 937 932 938 err = edac_create_dimm_object(mci, dimm); 933 939 if (err) 934 - goto fail_unregister_dimm; 940 + goto fail; 935 941 } 936 942 937 943 #ifdef CONFIG_EDAC_LEGACY_SYSFS 938 944 err = edac_create_csrow_objects(mci); 939 945 if (err < 0) 940 - goto fail_unregister_dimm; 946 + goto fail; 941 947 #endif 942 948 943 949 edac_create_debugfs_nodes(mci); 944 950 return 0; 945 951 946 - fail_unregister_dimm: 947 - mci_for_each_dimm(mci, dimm) { 948 - if (device_is_registered(&dimm->dev)) 949 - device_unregister(&dimm->dev); 950 - } 951 - device_unregister(&mci->dev); 952 + fail: 953 + edac_remove_sysfs_mci_device(mci); 952 954 953 955 return err; 954 956 } ··· 956 966 { 957 967 struct dimm_info *dimm; 958 968 969 + if (!device_is_registered(&mci->dev)) 970 + return; 971 + 959 972 edac_dbg(0, "\n"); 960 973 961 974 #ifdef CONFIG_EDAC_DEBUG ··· 969 976 #endif 970 977 971 978 mci_for_each_dimm(mci, dimm) { 972 - if (dimm->nr_pages == 0) 979 + if (!device_is_registered(&dimm->dev)) 973 980 continue; 974 981 edac_dbg(1, "unregistering device %s\n", dev_name(&dimm->dev)); 975 982 device_unregister(&dimm->dev); 976 983 } 977 - } 978 984 979 - void edac_unregister_sysfs(struct mem_ctl_info *mci) 980 - { 981 - edac_dbg(1, "unregistering device %s\n", dev_name(&mci->dev)); 982 - device_unregister(&mci->dev); 985 + /* only remove the device, but keep mci */ 986 + device_del(&mci->dev); 983 987 } 984 988 985 989 static void mc_attr_release(struct device *dev) ··· 990 1000 kfree(dev); 991 1001 } 992 1002 993 - static const struct device_type mc_attr_type = { 994 - .release = mc_attr_release, 995 - }; 996 1003 /* 997 1004 * Init/exit code for the module. Basically, creates/removes /sys/class/rc 998 1005 */ ··· 1002 1015 return -ENOMEM; 1003 1016 1004 1017 mci_pdev->bus = edac_get_sysfs_subsys(); 1005 - mci_pdev->type = &mc_attr_type; 1006 - device_initialize(mci_pdev); 1007 - dev_set_name(mci_pdev, "mc"); 1018 + mci_pdev->release = mc_attr_release; 1019 + mci_pdev->init_name = "mc"; 1008 1020 1009 - err = device_add(mci_pdev); 1021 + err = device_register(mci_pdev); 1010 1022 if (err < 0) { 1011 1023 edac_dbg(1, "failure: create device %s\n", dev_name(mci_pdev)); 1012 1024 put_device(mci_pdev);
-1
drivers/edac/edac_module.h
··· 28 28 extern int edac_create_sysfs_mci_device(struct mem_ctl_info *mci, 29 29 const struct attribute_group **groups); 30 30 extern void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci); 31 - void edac_unregister_sysfs(struct mem_ctl_info *mci); 32 31 extern int edac_get_log_ue(void); 33 32 extern int edac_get_log_ce(void); 34 33 extern int edac_get_panic_on_ue(void);