Example #1
0
/**
 * 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;
}
Example #4
0
/**
 * 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;
}
Example #5
0
/**
 * 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;
}
Example #6
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;
}
Example #8
0
/**
 * 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;
}