From: Nathan F. <nf...@au...> - 2010-08-16 14:48:03
|
Fixes issues with DLPAR on some p7 platforms. Signed-off-by: Brian King <br...@li...> --- src/drmgr/drslot_chrp_phb.c | 164 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 164 insertions(+) diff -puN src/drmgr/drslot_chrp_phb.c~dlpar_hp src/drmgr/drslot_chrp_phb.c --- powerpc-utils/src/drmgr/drslot_chrp_phb.c~dlpar_hp 2010-08-10 16:00:35.000000000 -0500 +++ powerpc-utils-bjking1/src/drmgr/drslot_chrp_phb.c 2010-08-10 17:55:22.000000000 -0500 @@ -8,6 +8,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <dirent.h> #include <librtas.h> #include <errno.h> #include "dr.h" @@ -87,6 +88,156 @@ release_phb(struct dr_node *phb) return rc; } +struct hpdev { + struct hpdev *next; + char path[256]; + char devspec[256]; +}; + +#define SYSFS_PCI_DEV_PATH "/sys/bus/pci/devices" + +static void free_hpdev_list(struct hpdev *hpdev_list) +{ + struct hpdev *hpdev; + + while (hpdev_list) { + hpdev = hpdev_list; + hpdev_list = hpdev_list->next; + free(hpdev); + } +} + +static int get_os_hp_devices(struct hpdev **hpdev_list) +{ + struct hpdev *hp_list = NULL; + struct hpdev *hpdev; + DIR *d; + struct dirent *de; + int rc = 0; + + d = opendir(SYSFS_PCI_DEV_PATH); + if (!d) { + err_msg("Failed to open %s\n", SYSFS_PCI_DEV_PATH); + return -1; + } + + while ((de = readdir(d)) != NULL) { + if (is_dot_dir(de->d_name)) + continue; + + hpdev = zalloc(sizeof(*hpdev)); + if (!hpdev) { + rc = -1; + break; + } + + rc = sprintf(hpdev->path, "%s/%s", SYSFS_PCI_DEV_PATH, + de->d_name); + if (rc < 0) + break; + + rc = get_str_attribute(hpdev->path, "devspec", hpdev->devspec, + 256); + if (rc) + break; + + dbg("HPDEV: %s\n %s\n", hpdev->path, hpdev->devspec); + hpdev->next = hp_list; + hp_list = hpdev; + } + + closedir(d); + + if (rc) { + free_hpdev_list(hp_list); + hp_list = NULL; + } + + *hpdev_list = hp_list; + return rc; +} + +static int hp_remove_os_device(struct hpdev *hpdev) +{ + FILE *file; + char path[256]; + int rc; + + sprintf(path, "%s/%s", hpdev->path, "remove"); + + file = fopen(path, "w"); + if (!file) + return -1; + + dbg("Removing %s\n", hpdev->path); + rc = fwrite("1", 1, 1, file); + if (rc == 1) + rc = 0; + + fclose(file); + sleep(5); + return rc; +} + +static int disable_os_hp_children_recurse(struct dr_node *phb, + struct hpdev *hpdev_list, char *ofpath) +{ + struct hpdev *hpdev; + DIR *d; + struct dirent *de; + int rc = 0; + + d = opendir(ofpath); + if (!d) + return -1; + + while ((de = readdir(d)) != NULL) { + char devspec[256]; + + if (is_dot_dir(de->d_name)) + continue; + + if (de->d_type == DT_DIR) { + char lpath[4096]; + sprintf(lpath, "%s/%s", ofpath, de->d_name); + rc = disable_os_hp_children_recurse(phb, hpdev_list, lpath); + } + + memset(devspec, 0, 256); + sprintf(devspec, "%s/%s", ofpath + strlen(OFDT_BASE), + de->d_name); + + for (hpdev = hpdev_list; hpdev; hpdev = hpdev->next) { + if (!strcmp(hpdev->devspec, devspec)) { + rc = hp_remove_os_device(hpdev); + break; + } + } + + if (rc) { + err_msg("Failed to hotplug remove %s\n", hpdev->path); + break; + } + } + + closedir(d); + return rc; +} + +static int disable_os_hp_children(struct dr_node *phb) +{ + struct hpdev *hpdev_list; + int rc = 0; + + rc = get_os_hp_devices(&hpdev_list); + if (rc) + return -1; + + rc = disable_os_hp_children_recurse(phb, hpdev_list, phb->ofdt_path); + free_hpdev_list(hpdev_list); + return rc; +} + /** * remove_phb * @@ -127,6 +278,19 @@ remove_phb(struct options *opts) } } + /* If there are any directories under the phb left at this point, + * they are OS hotplug devies. Note: this is different from DR + * hotplug devices. This really occurs on systems that do not + * support DR hotplug devices. The device tree does not get populated + * with drc information for these devices and such they do not appear + * on the list generated by the calls to get_node_* + * + * For these devices we simply hotplug remove them from the OS. + */ + rc = disable_os_hp_children(phb); + if (rc) + goto phb_remove_error; + rc = dlpar_io_kernel_op(dlpar_remove_slot, phb->drc_name); if (rc) { err_msg("kernel remove failed for %s, rc = %d\n", _ |