/** * remove_device_tree_lmb * @brief Update the device tree for the lmb being removed. * * @param lmb memory block to be removed from the device tree * @param lmb_list list of all lmbs * @returns 0 on success, !o otherwise */ static int remove_device_tree_lmb(struct dr_node *lmb, struct lmb_list_head *lmb_list) { if (lmb_list->drconf_buf) return update_drconf_node(lmb, lmb_list, REMOVE); return remove_device_tree_nodes(lmb->ofdt_path); }
/** * release_phb * */ static int release_phb(struct dr_node *phb) { int rc; rc = remove_device_tree_nodes(phb->ofdt_path); if (rc) return rc; if (phb->phb_ic_ofdt_path[0] != '\0') { rc = remove_device_tree_nodes(phb->phb_ic_ofdt_path); if (rc) return rc; } rc = release_drc(phb->drc_index, PHB_DEV); return rc; }
/** * cache_remove_devnode * @brief Remove the given cache device node from the kernel's device tree. * Also remove the cache from the global cache list. * * @param cache * @returns */ static int cache_remove_devnode(struct cache_info *cache) { int rc; rc = remove_device_tree_nodes(cache->path); if (!rc) cache->removed = 1; return rc; }
/** * remove_device_tree_nodes * * Remove all device nodes and children device nodes from Open Firmware * device tree * * @param root_path * @returns 0 on success, !0 otherwise */ int remove_device_tree_nodes(char *path) { DIR *d; struct dirent *de; struct stat sb; int found = 1; int rc; rc = lstat(path, &sb); if (rc || (!S_ISDIR(sb.st_mode)) || (S_ISLNK(sb.st_mode))) return rc; d = opendir(path); if (d == NULL) { say(ERROR, "Could not open %s: %s\n", path, strerror(errno)); return -1; } while (found) { char subdir_name[DR_PATH_MAX]; found = 0; /* Remove any subdirectories */ while ((de = readdir(d)) != NULL) { if (is_dot_dir(de->d_name)) continue; sprintf(subdir_name, "%s/%s", path, de->d_name); rc = lstat(subdir_name, &sb); if (!rc && (S_ISDIR(sb.st_mode)) && (!S_ISLNK(sb.st_mode))) { found = 1; break; } } if (found) { rc = remove_device_tree_nodes(subdir_name); rewinddir(d); } if (rc) break; } closedir(d); if (!rc) rc = remove_node(path); return rc; }
/** * release_hp_resource * * @param drc_index * @param power_domain * @returns 0 on success, !0 otherwise */ static int release_hp_resource(struct dr_node *node) { int rc; rc = remove_device_tree_nodes(node->ofdt_path); if (rc) { say(ERROR, "failed to remove kernel nodes for index 0x%x\n", node->drc_index); return -EIO; } if (pci_hotplug_only) return 0; rc = rtas_set_indicator(DR_INDICATOR, node->drc_index, LED_OFF); if (rc) { say(ERROR, "failed to set led off for index 0x%x\n", node->drc_index); return -EIO; } rc = rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE); if (rc) { say(ERROR, "failed to isolate for index 0x%x\n", node->drc_index); return -EIO; } rc = set_power(node->drc_power, POWER_OFF); if (rc) { struct stat sb; say(ERROR, "failed to power off for index 0x%x\n", node->drc_index); if (!stat(IGNORE_HP_PO_PROP, &sb)) say(ERROR, "Ignoring hot-plug power off failure.\n"); else return -EIO; } return 0; }
/** * remove_work * * Removes nodes associated a PCI slot from the * Open Firmware device tree and isolates and powers off the PCI slot. * * A remove may be specified by the location code of the PCI slot. * Unless the user specifies the -I flag, the slot is identified to * the user. * Nodes representing the device(s) are removed from the * Open Firmware device tree. The slot is isolated and powered off, * and the LED is turned off. * * @returns pointer slot on success, NULL on failure */ static struct dr_node * remove_work(struct options *opts, struct dr_node *all_nodes) { struct dr_node *node; struct dr_node *child; int rc; int usr_key = USER_CONT; node = find_slot(opts->usr_drc_name, all_nodes); if (node == NULL) return NULL; say(DEBUG, "found node: drc name=%s, index=0x%x, path=%s\n", node->drc_name, node->drc_index, node->ofdt_path); /* Prompt user only if not in noprompt mode */ if (0 == opts->noprompt) { if (!opts->no_ident) usr_key = identify_slot(node); if (usr_key == USER_QUIT) { if (node->children == NULL) process_led(node, LED_OFF); else process_led(node, LED_ON); return NULL; } } /* Turn on the LED while we go do some work. */ if (process_led(node, LED_ON)) return NULL; /* Make sure there's something there to remove. */ if (node->children == NULL) { process_led(node, LED_OFF); say(ERROR, "There is no configured card to remove from the " "specified PCI slot.\n"); return NULL; } /* Make sure all the devices are * not configured before proceeding */ if (get_hp_adapter_status(node->drc_name) == CONFIG) { say(DEBUG, "unconfiguring adapter in slot[%s]\n", node->drc_name); set_hp_adapter_status(PHP_UNCONFIG_ADAPTER, node->drc_name); int rc = get_hp_adapter_status(node->drc_name); if (rc != NOT_CONFIG) { say(ERROR, "Unconfig adapter failed.\n"); return NULL; } } else { /* In certain cases such as a complete failure of the * adapter there may not have been the possibility to clean * up everything. Mark these adapaters for additional * processing later. */ node->post_replace_processing = 1; } /* Call subroutine to remove node(s) from * the device tree. */ for (child = node->children; child; child = child->next) { rc = remove_device_tree_nodes(child->ofdt_path); if (rc) { say(ERROR, "%s", sw_error); rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE); set_power(node->drc_power, POWER_OFF); return NULL; } } /* We have to isolate and power off before * allowing the user to physically remove * the card. */ say(DEBUG, "is calling rtas_set_indicator(ISOLATE index 0x%x)\n", node->drc_index); rc = rtas_set_indicator(ISOLATION_STATE, node->drc_index, ISOLATE); if (rc) { if (rc == HW_ERROR) say(ERROR, "%s", hw_error); else say(ERROR, "%s", sw_error); set_power(node->drc_power, POWER_OFF); return NULL; } say(DEBUG, "is calling set_power(POWER_OFF index 0x%x, power_domain " "0x%x)\n", node->drc_index, node->drc_power); rc = set_power(node->drc_power, POWER_OFF); if (rc) { if (rc == HW_ERROR) say(ERROR, "%s", hw_error); else say(ERROR, "%s", sw_error); set_power(node->drc_power, POWER_OFF); return NULL; } return node; }
/** * release_cpu * * Release a cpu back to the hypervisor, using the cpu's drc-index. * The cpu must have already been offlined. We remove the cpu from the * device tree only when the isolate and set_indicator have succeeded. * The caller must not use the value given for the cpu parameter after * this function has returned. * * @param cpu * @returns 0 on success, !0 otherwise */ int release_cpu(struct dr_node *cpu, struct dr_info *dr_info) { int release_file; int rc; release_file = open(CPU_RELEASE_FILE, O_WRONLY); if (release_file > 0) { /* DLPAR can be done in kernel */ char *path = cpu->ofdt_path + strlen(OFDT_BASE); int write_len = strlen(path); say(DEBUG, "Releasing cpu \"%s\"\n", path); rc = write(release_file, path, write_len); if (rc != write_len) say(ERROR, "Release failed! rc = %d\n", rc); else /* set rc to success */ rc = 0; close(release_file); } else { /* Must do DLPAR from user-space */ rc = offline_cpu(cpu); if (rc) { say(ERROR, "Could not offline cpu %s\n", cpu->drc_name); return rc; } rc = release_drc(cpu->drc_index, CPU_DEV); if (rc) { say(ERROR, "Could not release drc resources for %s\n", cpu->name); return rc; } rc = remove_device_tree_nodes(cpu->ofdt_path); if (rc) { struct of_node *of_nodes; say(ERROR, "Could not remove device tree nodes %s\n", cpu->name); of_nodes = configure_connector(cpu->drc_index); if (of_nodes == NULL) { say(ERROR, "Call to configure_connector failed " "for %s. The device tree\nmay contain " "invalid data for this cpu and a " "re-activation of the partition is " "needed to correct it.\n", cpu->name); } else { rc = add_device_tree_nodes(CPU_OFDT_BASE, of_nodes); free_of_node(of_nodes); } acquire_drc(cpu->drc_index); return rc; } release_caches(cpu, dr_info); } return rc; }
/** * add_device_tree_lmb * @brief Update the device tree for the lmb being added.. * * @param lmb lmb to acquire * @param lmb_list list of all lmbs * @returns 0 on success, !0 otherwise */ static int add_device_tree_lmb(struct dr_node *lmb, struct lmb_list_head *lmb_list) { int rc; lmb->lmb_of_node = configure_connector(lmb->drc_index); if (lmb->lmb_of_node == NULL) { release_drc(lmb->drc_index, MEM_DEV); return -1; } if (lmb_list->drconf_buf) { errno = 0; rc = update_drconf_node(lmb, lmb_list, ADD); if (errno == ENODEV) { /* Due to bug in pre 2.6.27 kernels, updating the * property in the device tree fails when the phandle * is processed as a signed int instead of unsigned * In this case we provide this little hack to allow * memory add to work on these kernels. */ say(DEBUG, "Assuming older kernel, trying to add " "node\n"); sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE, lmb->lmb_of_node->name); rc = add_device_tree_nodes(OFDT_BASE, lmb->lmb_of_node); } else { sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE, "/ibm,dynamic-reconfiguration-memory"); } } else { /* Add the new nodes to the device tree */ sprintf(lmb->ofdt_path, "%s/%s", OFDT_BASE, lmb->lmb_of_node->name); rc = add_device_tree_nodes(OFDT_BASE, lmb->lmb_of_node); } if (rc) return rc; if (! lmb_list->drconf_buf) { /* Find the physical address for this lmb. This is only * needed for non-drconf memory. The address for drconf * lmbs is correctly initialized when building the lmb list */ char *tmp = strrchr(lmb->ofdt_path, '@'); if (tmp == NULL) { say(DEBUG, "Could not determine physical address for " "%s\n", lmb->ofdt_path); remove_device_tree_nodes(lmb->ofdt_path); return -1; } lmb->lmb_address = strtoull(tmp + 1, NULL, 16); rc = get_lmb_size(lmb); if (rc) { remove_device_tree_nodes(lmb->ofdt_path); return rc; } } rc = get_mem_scns(lmb); if (rc) remove_device_tree_lmb(lmb, lmb_list); return rc; }