| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 1 | /* | 
 | 2 |  *  drivers/s390/cio/chp.c | 
 | 3 |  * | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 4 |  *    Copyright IBM Corp. 1999,2010 | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 5 |  *    Author(s): Cornelia Huck (cornelia.huck@de.ibm.com) | 
 | 6 |  *		 Arnd Bergmann (arndb@de.ibm.com) | 
 | 7 |  *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 
 | 8 |  */ | 
 | 9 |  | 
 | 10 | #include <linux/bug.h> | 
 | 11 | #include <linux/workqueue.h> | 
 | 12 | #include <linux/spinlock.h> | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 13 | #include <linux/init.h> | 
 | 14 | #include <linux/jiffies.h> | 
 | 15 | #include <linux/wait.h> | 
 | 16 | #include <linux/mutex.h> | 
| Cornelia Huck | a0ea22c3 | 2007-10-12 16:11:19 +0200 | [diff] [blame] | 17 | #include <linux/errno.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 18 | #include <linux/slab.h> | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 19 | #include <asm/chpid.h> | 
 | 20 | #include <asm/sclp.h> | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 21 | #include <asm/crw.h> | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 22 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 23 | #include "cio.h" | 
 | 24 | #include "css.h" | 
 | 25 | #include "ioasm.h" | 
 | 26 | #include "cio_debug.h" | 
 | 27 | #include "chp.h" | 
 | 28 |  | 
 | 29 | #define to_channelpath(device) container_of(device, struct channel_path, dev) | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 30 | #define CHP_INFO_UPDATE_INTERVAL	1*HZ | 
 | 31 |  | 
 | 32 | enum cfg_task_t { | 
 | 33 | 	cfg_none, | 
 | 34 | 	cfg_configure, | 
 | 35 | 	cfg_deconfigure | 
 | 36 | }; | 
 | 37 |  | 
 | 38 | /* Map for pending configure tasks. */ | 
 | 39 | static enum cfg_task_t chp_cfg_task[__MAX_CSSID + 1][__MAX_CHPID + 1]; | 
 | 40 | static DEFINE_MUTEX(cfg_lock); | 
 | 41 | static int cfg_busy; | 
 | 42 |  | 
 | 43 | /* Map for channel-path status. */ | 
 | 44 | static struct sclp_chp_info chp_info; | 
 | 45 | static DEFINE_MUTEX(info_lock); | 
 | 46 |  | 
 | 47 | /* Time after which channel-path status may be outdated. */ | 
 | 48 | static unsigned long chp_info_expires; | 
 | 49 |  | 
 | 50 | /* Workqueue to perform pending configure tasks. */ | 
 | 51 | static struct workqueue_struct *chp_wq; | 
 | 52 | static struct work_struct cfg_work; | 
 | 53 |  | 
 | 54 | /* Wait queue for configure completion events. */ | 
 | 55 | static wait_queue_head_t cfg_wait_queue; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 56 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 57 | /* Set vary state for given chpid. */ | 
 | 58 | static void set_chp_logically_online(struct chp_id chpid, int onoff) | 
 | 59 | { | 
 | 60 | 	chpid_to_chp(chpid)->state = onoff; | 
 | 61 | } | 
 | 62 |  | 
| André Goddard Rosa | af901ca | 2009-11-14 13:09:05 -0200 | [diff] [blame] | 63 | /* On success return 0 if channel-path is varied offline, 1 if it is varied | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 64 |  * online. Return -ENODEV if channel-path is not registered. */ | 
 | 65 | int chp_get_status(struct chp_id chpid) | 
 | 66 | { | 
 | 67 | 	return (chpid_to_chp(chpid) ? chpid_to_chp(chpid)->state : -ENODEV); | 
 | 68 | } | 
 | 69 |  | 
 | 70 | /** | 
 | 71 |  * chp_get_sch_opm - return opm for subchannel | 
 | 72 |  * @sch: subchannel | 
 | 73 |  * | 
 | 74 |  * Calculate and return the operational path mask (opm) based on the chpids | 
 | 75 |  * used by the subchannel and the status of the associated channel-paths. | 
 | 76 |  */ | 
 | 77 | u8 chp_get_sch_opm(struct subchannel *sch) | 
 | 78 | { | 
 | 79 | 	struct chp_id chpid; | 
 | 80 | 	int opm; | 
 | 81 | 	int i; | 
 | 82 |  | 
 | 83 | 	opm = 0; | 
 | 84 | 	chp_id_init(&chpid); | 
| Cornelia Huck | a0ea22c3 | 2007-10-12 16:11:19 +0200 | [diff] [blame] | 85 | 	for (i = 0; i < 8; i++) { | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 86 | 		opm <<= 1; | 
 | 87 | 		chpid.id = sch->schib.pmcw.chpid[i]; | 
 | 88 | 		if (chp_get_status(chpid) != 0) | 
 | 89 | 			opm |= 1; | 
 | 90 | 	} | 
 | 91 | 	return opm; | 
 | 92 | } | 
| Cornelia Huck | 44a1c19 | 2008-07-14 09:58:47 +0200 | [diff] [blame] | 93 | EXPORT_SYMBOL_GPL(chp_get_sch_opm); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 94 |  | 
 | 95 | /** | 
 | 96 |  * chp_is_registered - check if a channel-path is registered | 
 | 97 |  * @chpid: channel-path ID | 
 | 98 |  * | 
 | 99 |  * Return non-zero if a channel-path with the given chpid is registered, | 
 | 100 |  * zero otherwise. | 
 | 101 |  */ | 
 | 102 | int chp_is_registered(struct chp_id chpid) | 
 | 103 | { | 
 | 104 | 	return chpid_to_chp(chpid) != NULL; | 
 | 105 | } | 
 | 106 |  | 
 | 107 | /* | 
 | 108 |  * Function: s390_vary_chpid | 
 | 109 |  * Varies the specified chpid online or offline | 
 | 110 |  */ | 
 | 111 | static int s390_vary_chpid(struct chp_id chpid, int on) | 
 | 112 | { | 
 | 113 | 	char dbf_text[15]; | 
 | 114 | 	int status; | 
 | 115 |  | 
 | 116 | 	sprintf(dbf_text, on?"varyon%x.%02x":"varyoff%x.%02x", chpid.cssid, | 
 | 117 | 		chpid.id); | 
| Cornelia Huck | a0ea22c3 | 2007-10-12 16:11:19 +0200 | [diff] [blame] | 118 | 	CIO_TRACE_EVENT(2, dbf_text); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 119 |  | 
 | 120 | 	status = chp_get_status(chpid); | 
| Michael Ernst | c78aa6c | 2008-07-14 09:59:22 +0200 | [diff] [blame] | 121 | 	if (!on && !status) | 
 | 122 | 		return 0; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 123 |  | 
 | 124 | 	set_chp_logically_online(chpid, on); | 
 | 125 | 	chsc_chp_vary(chpid, on); | 
 | 126 | 	return 0; | 
 | 127 | } | 
 | 128 |  | 
 | 129 | /* | 
 | 130 |  * Channel measurement related functions | 
 | 131 |  */ | 
| Chris Wright | 2c3c8be | 2010-05-12 18:28:57 -0700 | [diff] [blame] | 132 | static ssize_t chp_measurement_chars_read(struct file *filp, | 
 | 133 | 					  struct kobject *kobj, | 
| Zhang Rui | 91a6902 | 2007-06-09 13:57:22 +0800 | [diff] [blame] | 134 | 					  struct bin_attribute *bin_attr, | 
 | 135 | 					  char *buf, loff_t off, size_t count) | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 136 | { | 
 | 137 | 	struct channel_path *chp; | 
| Heiko Carstens | 364c855 | 2007-10-12 16:11:35 +0200 | [diff] [blame] | 138 | 	struct device *device; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 139 |  | 
| Heiko Carstens | 364c855 | 2007-10-12 16:11:35 +0200 | [diff] [blame] | 140 | 	device = container_of(kobj, struct device, kobj); | 
 | 141 | 	chp = to_channelpath(device); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 142 | 	if (!chp->cmg_chars) | 
 | 143 | 		return 0; | 
 | 144 |  | 
| Akinobu Mita | d9cef21 | 2008-07-14 09:59:15 +0200 | [diff] [blame] | 145 | 	return memory_read_from_buffer(buf, count, &off, | 
 | 146 | 				chp->cmg_chars, sizeof(struct cmg_chars)); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 147 | } | 
 | 148 |  | 
 | 149 | static struct bin_attribute chp_measurement_chars_attr = { | 
 | 150 | 	.attr = { | 
 | 151 | 		.name = "measurement_chars", | 
 | 152 | 		.mode = S_IRUSR, | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 153 | 	}, | 
 | 154 | 	.size = sizeof(struct cmg_chars), | 
 | 155 | 	.read = chp_measurement_chars_read, | 
 | 156 | }; | 
 | 157 |  | 
 | 158 | static void chp_measurement_copy_block(struct cmg_entry *buf, | 
 | 159 | 				       struct channel_subsystem *css, | 
 | 160 | 				       struct chp_id chpid) | 
 | 161 | { | 
 | 162 | 	void *area; | 
 | 163 | 	struct cmg_entry *entry, reference_buf; | 
 | 164 | 	int idx; | 
 | 165 |  | 
 | 166 | 	if (chpid.id < 128) { | 
 | 167 | 		area = css->cub_addr1; | 
 | 168 | 		idx = chpid.id; | 
 | 169 | 	} else { | 
 | 170 | 		area = css->cub_addr2; | 
 | 171 | 		idx = chpid.id - 128; | 
 | 172 | 	} | 
 | 173 | 	entry = area + (idx * sizeof(struct cmg_entry)); | 
 | 174 | 	do { | 
 | 175 | 		memcpy(buf, entry, sizeof(*entry)); | 
 | 176 | 		memcpy(&reference_buf, entry, sizeof(*entry)); | 
 | 177 | 	} while (reference_buf.values[0] != buf->values[0]); | 
 | 178 | } | 
 | 179 |  | 
| Chris Wright | 2c3c8be | 2010-05-12 18:28:57 -0700 | [diff] [blame] | 180 | static ssize_t chp_measurement_read(struct file *filp, struct kobject *kobj, | 
| Zhang Rui | 91a6902 | 2007-06-09 13:57:22 +0800 | [diff] [blame] | 181 | 				    struct bin_attribute *bin_attr, | 
 | 182 | 				    char *buf, loff_t off, size_t count) | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 183 | { | 
 | 184 | 	struct channel_path *chp; | 
 | 185 | 	struct channel_subsystem *css; | 
| Heiko Carstens | 364c855 | 2007-10-12 16:11:35 +0200 | [diff] [blame] | 186 | 	struct device *device; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 187 | 	unsigned int size; | 
 | 188 |  | 
| Heiko Carstens | 364c855 | 2007-10-12 16:11:35 +0200 | [diff] [blame] | 189 | 	device = container_of(kobj, struct device, kobj); | 
 | 190 | 	chp = to_channelpath(device); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 191 | 	css = to_css(chp->dev.parent); | 
 | 192 |  | 
 | 193 | 	size = sizeof(struct cmg_entry); | 
 | 194 |  | 
 | 195 | 	/* Only allow single reads. */ | 
 | 196 | 	if (off || count < size) | 
 | 197 | 		return 0; | 
 | 198 | 	chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->chpid); | 
 | 199 | 	count = size; | 
 | 200 | 	return count; | 
 | 201 | } | 
 | 202 |  | 
 | 203 | static struct bin_attribute chp_measurement_attr = { | 
 | 204 | 	.attr = { | 
 | 205 | 		.name = "measurement", | 
 | 206 | 		.mode = S_IRUSR, | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 207 | 	}, | 
 | 208 | 	.size = sizeof(struct cmg_entry), | 
 | 209 | 	.read = chp_measurement_read, | 
 | 210 | }; | 
 | 211 |  | 
 | 212 | void chp_remove_cmg_attr(struct channel_path *chp) | 
 | 213 | { | 
 | 214 | 	device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); | 
 | 215 | 	device_remove_bin_file(&chp->dev, &chp_measurement_attr); | 
 | 216 | } | 
 | 217 |  | 
 | 218 | int chp_add_cmg_attr(struct channel_path *chp) | 
 | 219 | { | 
 | 220 | 	int ret; | 
 | 221 |  | 
 | 222 | 	ret = device_create_bin_file(&chp->dev, &chp_measurement_chars_attr); | 
 | 223 | 	if (ret) | 
 | 224 | 		return ret; | 
 | 225 | 	ret = device_create_bin_file(&chp->dev, &chp_measurement_attr); | 
 | 226 | 	if (ret) | 
 | 227 | 		device_remove_bin_file(&chp->dev, &chp_measurement_chars_attr); | 
 | 228 | 	return ret; | 
 | 229 | } | 
 | 230 |  | 
 | 231 | /* | 
 | 232 |  * Files for the channel path entries. | 
 | 233 |  */ | 
 | 234 | static ssize_t chp_status_show(struct device *dev, | 
 | 235 | 			       struct device_attribute *attr, char *buf) | 
 | 236 | { | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 237 | 	struct channel_path *chp = to_channelpath(dev); | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 238 | 	int status; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 239 |  | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 240 | 	mutex_lock(&chp->lock); | 
 | 241 | 	status = chp->state; | 
 | 242 | 	mutex_unlock(&chp->lock); | 
 | 243 |  | 
 | 244 | 	return status ? sprintf(buf, "online\n") : sprintf(buf, "offline\n"); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 245 | } | 
 | 246 |  | 
 | 247 | static ssize_t chp_status_write(struct device *dev, | 
 | 248 | 				struct device_attribute *attr, | 
 | 249 | 				const char *buf, size_t count) | 
 | 250 | { | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 251 | 	struct channel_path *cp = to_channelpath(dev); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 252 | 	char cmd[10]; | 
 | 253 | 	int num_args; | 
 | 254 | 	int error; | 
 | 255 |  | 
 | 256 | 	num_args = sscanf(buf, "%5s", cmd); | 
 | 257 | 	if (!num_args) | 
 | 258 | 		return count; | 
 | 259 |  | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 260 | 	if (!strnicmp(cmd, "on", 2) || !strcmp(cmd, "1")) { | 
 | 261 | 		mutex_lock(&cp->lock); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 262 | 		error = s390_vary_chpid(cp->chpid, 1); | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 263 | 		mutex_unlock(&cp->lock); | 
 | 264 | 	} else if (!strnicmp(cmd, "off", 3) || !strcmp(cmd, "0")) { | 
 | 265 | 		mutex_lock(&cp->lock); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 266 | 		error = s390_vary_chpid(cp->chpid, 0); | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 267 | 		mutex_unlock(&cp->lock); | 
 | 268 | 	} else | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 269 | 		error = -EINVAL; | 
 | 270 |  | 
 | 271 | 	return error < 0 ? error : count; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 272 | } | 
 | 273 |  | 
 | 274 | static DEVICE_ATTR(status, 0644, chp_status_show, chp_status_write); | 
 | 275 |  | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 276 | static ssize_t chp_configure_show(struct device *dev, | 
 | 277 | 				  struct device_attribute *attr, char *buf) | 
 | 278 | { | 
 | 279 | 	struct channel_path *cp; | 
 | 280 | 	int status; | 
 | 281 |  | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 282 | 	cp = to_channelpath(dev); | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 283 | 	status = chp_info_get_status(cp->chpid); | 
 | 284 | 	if (status < 0) | 
 | 285 | 		return status; | 
 | 286 |  | 
 | 287 | 	return snprintf(buf, PAGE_SIZE, "%d\n", status); | 
 | 288 | } | 
 | 289 |  | 
 | 290 | static int cfg_wait_idle(void); | 
 | 291 |  | 
 | 292 | static ssize_t chp_configure_write(struct device *dev, | 
 | 293 | 				   struct device_attribute *attr, | 
 | 294 | 				   const char *buf, size_t count) | 
 | 295 | { | 
 | 296 | 	struct channel_path *cp; | 
 | 297 | 	int val; | 
 | 298 | 	char delim; | 
 | 299 |  | 
 | 300 | 	if (sscanf(buf, "%d %c", &val, &delim) != 1) | 
 | 301 | 		return -EINVAL; | 
 | 302 | 	if (val != 0 && val != 1) | 
 | 303 | 		return -EINVAL; | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 304 | 	cp = to_channelpath(dev); | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 305 | 	chp_cfg_schedule(cp->chpid, val); | 
 | 306 | 	cfg_wait_idle(); | 
 | 307 |  | 
 | 308 | 	return count; | 
 | 309 | } | 
 | 310 |  | 
 | 311 | static DEVICE_ATTR(configure, 0644, chp_configure_show, chp_configure_write); | 
 | 312 |  | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 313 | static ssize_t chp_type_show(struct device *dev, struct device_attribute *attr, | 
 | 314 | 			     char *buf) | 
 | 315 | { | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 316 | 	struct channel_path *chp = to_channelpath(dev); | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 317 | 	u8 type; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 318 |  | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 319 | 	mutex_lock(&chp->lock); | 
 | 320 | 	type = chp->desc.desc; | 
 | 321 | 	mutex_unlock(&chp->lock); | 
 | 322 | 	return sprintf(buf, "%x\n", type); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 323 | } | 
 | 324 |  | 
 | 325 | static DEVICE_ATTR(type, 0444, chp_type_show, NULL); | 
 | 326 |  | 
 | 327 | static ssize_t chp_cmg_show(struct device *dev, struct device_attribute *attr, | 
 | 328 | 			    char *buf) | 
 | 329 | { | 
 | 330 | 	struct channel_path *chp = to_channelpath(dev); | 
 | 331 |  | 
 | 332 | 	if (!chp) | 
 | 333 | 		return 0; | 
 | 334 | 	if (chp->cmg == -1) /* channel measurements not available */ | 
 | 335 | 		return sprintf(buf, "unknown\n"); | 
 | 336 | 	return sprintf(buf, "%x\n", chp->cmg); | 
 | 337 | } | 
 | 338 |  | 
 | 339 | static DEVICE_ATTR(cmg, 0444, chp_cmg_show, NULL); | 
 | 340 |  | 
 | 341 | static ssize_t chp_shared_show(struct device *dev, | 
 | 342 | 			       struct device_attribute *attr, char *buf) | 
 | 343 | { | 
 | 344 | 	struct channel_path *chp = to_channelpath(dev); | 
 | 345 |  | 
 | 346 | 	if (!chp) | 
 | 347 | 		return 0; | 
 | 348 | 	if (chp->shared == -1) /* channel measurements not available */ | 
 | 349 | 		return sprintf(buf, "unknown\n"); | 
 | 350 | 	return sprintf(buf, "%x\n", chp->shared); | 
 | 351 | } | 
 | 352 |  | 
 | 353 | static DEVICE_ATTR(shared, 0444, chp_shared_show, NULL); | 
 | 354 |  | 
| Cornelia Huck | a0ea22c3 | 2007-10-12 16:11:19 +0200 | [diff] [blame] | 355 | static struct attribute *chp_attrs[] = { | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 356 | 	&dev_attr_status.attr, | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 357 | 	&dev_attr_configure.attr, | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 358 | 	&dev_attr_type.attr, | 
 | 359 | 	&dev_attr_cmg.attr, | 
 | 360 | 	&dev_attr_shared.attr, | 
 | 361 | 	NULL, | 
 | 362 | }; | 
 | 363 |  | 
 | 364 | static struct attribute_group chp_attr_group = { | 
 | 365 | 	.attrs = chp_attrs, | 
 | 366 | }; | 
 | 367 |  | 
 | 368 | static void chp_release(struct device *dev) | 
 | 369 | { | 
 | 370 | 	struct channel_path *cp; | 
 | 371 |  | 
| Cornelia Huck | 0546960 | 2007-10-22 12:52:40 +0200 | [diff] [blame] | 372 | 	cp = to_channelpath(dev); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 373 | 	kfree(cp); | 
 | 374 | } | 
 | 375 |  | 
 | 376 | /** | 
 | 377 |  * chp_new - register a new channel-path | 
 | 378 |  * @chpid - channel-path ID | 
 | 379 |  * | 
 | 380 |  * Create and register data structure representing new channel-path. Return | 
 | 381 |  * zero on success, non-zero otherwise. | 
 | 382 |  */ | 
 | 383 | int chp_new(struct chp_id chpid) | 
 | 384 | { | 
 | 385 | 	struct channel_path *chp; | 
 | 386 | 	int ret; | 
 | 387 |  | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 388 | 	if (chp_is_registered(chpid)) | 
 | 389 | 		return 0; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 390 | 	chp = kzalloc(sizeof(struct channel_path), GFP_KERNEL); | 
 | 391 | 	if (!chp) | 
 | 392 | 		return -ENOMEM; | 
 | 393 |  | 
 | 394 | 	/* fill in status, etc. */ | 
 | 395 | 	chp->chpid = chpid; | 
 | 396 | 	chp->state = 1; | 
| Cornelia Huck | 7c9f4e3 | 2007-10-12 16:11:13 +0200 | [diff] [blame] | 397 | 	chp->dev.parent = &channel_subsystems[chpid.cssid]->device; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 398 | 	chp->dev.release = chp_release; | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 399 | 	mutex_init(&chp->lock); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 400 |  | 
 | 401 | 	/* Obtain channel path description and fill it in. */ | 
| Cornelia Huck | 9d92a7e | 2008-07-14 09:59:05 +0200 | [diff] [blame] | 402 | 	ret = chsc_determine_base_channel_path_desc(chpid, &chp->desc); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 403 | 	if (ret) | 
 | 404 | 		goto out_free; | 
| Peter Oberparleiter | c9182e0 | 2007-04-27 16:01:29 +0200 | [diff] [blame] | 405 | 	if ((chp->desc.flags & 0x80) == 0) { | 
 | 406 | 		ret = -ENODEV; | 
 | 407 | 		goto out_free; | 
 | 408 | 	} | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 409 | 	/* Get channel-measurement characteristics. */ | 
| Cornelia Huck | 75784c0 | 2008-07-14 09:58:57 +0200 | [diff] [blame] | 410 | 	if (css_chsc_characteristics.scmc && css_chsc_characteristics.secm) { | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 411 | 		ret = chsc_get_channel_measurement_chars(chp); | 
 | 412 | 		if (ret) | 
 | 413 | 			goto out_free; | 
 | 414 | 	} else { | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 415 | 		chp->cmg = -1; | 
 | 416 | 	} | 
| Michael Ernst | ec00440 | 2009-10-06 10:33:59 +0200 | [diff] [blame] | 417 | 	dev_set_name(&chp->dev, "chp%x.%02x", chpid.cssid, chpid.id); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 418 |  | 
 | 419 | 	/* make it known to the system */ | 
 | 420 | 	ret = device_register(&chp->dev); | 
 | 421 | 	if (ret) { | 
| Cornelia Huck | e556bbb | 2007-07-27 12:29:19 +0200 | [diff] [blame] | 422 | 		CIO_MSG_EVENT(0, "Could not register chp%x.%02x: %d\n", | 
 | 423 | 			      chpid.cssid, chpid.id, ret); | 
| Sebastian Ott | c630493 | 2009-09-11 10:28:38 +0200 | [diff] [blame] | 424 | 		put_device(&chp->dev); | 
 | 425 | 		goto out; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 426 | 	} | 
 | 427 | 	ret = sysfs_create_group(&chp->dev.kobj, &chp_attr_group); | 
 | 428 | 	if (ret) { | 
 | 429 | 		device_unregister(&chp->dev); | 
| Cornelia Huck | a2164b8 | 2008-09-09 12:38:57 +0200 | [diff] [blame] | 430 | 		goto out; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 431 | 	} | 
| Cornelia Huck | 7c9f4e3 | 2007-10-12 16:11:13 +0200 | [diff] [blame] | 432 | 	mutex_lock(&channel_subsystems[chpid.cssid]->mutex); | 
 | 433 | 	if (channel_subsystems[chpid.cssid]->cm_enabled) { | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 434 | 		ret = chp_add_cmg_attr(chp); | 
 | 435 | 		if (ret) { | 
 | 436 | 			sysfs_remove_group(&chp->dev.kobj, &chp_attr_group); | 
 | 437 | 			device_unregister(&chp->dev); | 
| Cornelia Huck | 7c9f4e3 | 2007-10-12 16:11:13 +0200 | [diff] [blame] | 438 | 			mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); | 
| Cornelia Huck | a2164b8 | 2008-09-09 12:38:57 +0200 | [diff] [blame] | 439 | 			goto out; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 440 | 		} | 
 | 441 | 	} | 
| Cornelia Huck | 7c9f4e3 | 2007-10-12 16:11:13 +0200 | [diff] [blame] | 442 | 	channel_subsystems[chpid.cssid]->chps[chpid.id] = chp; | 
 | 443 | 	mutex_unlock(&channel_subsystems[chpid.cssid]->mutex); | 
| Cornelia Huck | a2164b8 | 2008-09-09 12:38:57 +0200 | [diff] [blame] | 444 | 	goto out; | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 445 | out_free: | 
 | 446 | 	kfree(chp); | 
| Cornelia Huck | a2164b8 | 2008-09-09 12:38:57 +0200 | [diff] [blame] | 447 | out: | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 448 | 	return ret; | 
 | 449 | } | 
 | 450 |  | 
 | 451 | /** | 
 | 452 |  * chp_get_chp_desc - return newly allocated channel-path description | 
 | 453 |  * @chpid: channel-path ID | 
 | 454 |  * | 
 | 455 |  * On success return a newly allocated copy of the channel-path description | 
 | 456 |  * data associated with the given channel-path ID. Return %NULL on error. | 
 | 457 |  */ | 
 | 458 | void *chp_get_chp_desc(struct chp_id chpid) | 
 | 459 | { | 
 | 460 | 	struct channel_path *chp; | 
 | 461 | 	struct channel_path_desc *desc; | 
 | 462 |  | 
 | 463 | 	chp = chpid_to_chp(chpid); | 
 | 464 | 	if (!chp) | 
 | 465 | 		return NULL; | 
 | 466 | 	desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL); | 
 | 467 | 	if (!desc) | 
 | 468 | 		return NULL; | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 469 |  | 
 | 470 | 	mutex_lock(&chp->lock); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 471 | 	memcpy(desc, &chp->desc, sizeof(struct channel_path_desc)); | 
| Sebastian Ott | b730f3a | 2010-10-25 16:10:27 +0200 | [diff] [blame] | 472 | 	mutex_unlock(&chp->lock); | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 473 | 	return desc; | 
 | 474 | } | 
 | 475 |  | 
 | 476 | /** | 
 | 477 |  * chp_process_crw - process channel-path status change | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 478 |  * @crw0: channel report-word to handler | 
 | 479 |  * @crw1: second channel-report word (always NULL) | 
 | 480 |  * @overflow: crw overflow indication | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 481 |  * | 
 | 482 |  * Handle channel-report-words indicating that the status of a channel-path | 
 | 483 |  * has changed. | 
 | 484 |  */ | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 485 | static void chp_process_crw(struct crw *crw0, struct crw *crw1, | 
 | 486 | 			    int overflow) | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 487 | { | 
 | 488 | 	struct chp_id chpid; | 
 | 489 |  | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 490 | 	if (overflow) { | 
 | 491 | 		css_schedule_eval_all(); | 
 | 492 | 		return; | 
 | 493 | 	} | 
 | 494 | 	CIO_CRW_EVENT(2, "CRW reports slct=%d, oflw=%d, " | 
 | 495 | 		      "chn=%d, rsc=%X, anc=%d, erc=%X, rsid=%X\n", | 
 | 496 | 		      crw0->slct, crw0->oflw, crw0->chn, crw0->rsc, crw0->anc, | 
 | 497 | 		      crw0->erc, crw0->rsid); | 
 | 498 | 	/* | 
 | 499 | 	 * Check for solicited machine checks. These are | 
 | 500 | 	 * created by reset channel path and need not be | 
 | 501 | 	 * handled here. | 
 | 502 | 	 */ | 
 | 503 | 	if (crw0->slct) { | 
 | 504 | 		CIO_CRW_EVENT(2, "solicited machine check for " | 
 | 505 | 			      "channel path %02X\n", crw0->rsid); | 
 | 506 | 		return; | 
 | 507 | 	} | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 508 | 	chp_id_init(&chpid); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 509 | 	chpid.id = crw0->rsid; | 
 | 510 | 	switch (crw0->erc) { | 
 | 511 | 	case CRW_ERC_IPARM: /* Path has come. */ | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 512 | 		if (!chp_is_registered(chpid)) | 
 | 513 | 			chp_new(chpid); | 
| Peter Oberparleiter | 83b3370 | 2007-04-27 16:01:34 +0200 | [diff] [blame] | 514 | 		chsc_chp_online(chpid); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 515 | 		break; | 
 | 516 | 	case CRW_ERC_PERRI: /* Path has gone. */ | 
 | 517 | 	case CRW_ERC_PERRN: | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 518 | 		chsc_chp_offline(chpid); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 519 | 		break; | 
 | 520 | 	default: | 
 | 521 | 		CIO_CRW_EVENT(2, "Don't know how to handle erc=%x\n", | 
 | 522 | 			      crw0->erc); | 
 | 523 | 	} | 
| Peter Oberparleiter | e6b6e10 | 2007-04-27 16:01:28 +0200 | [diff] [blame] | 524 | } | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 525 |  | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 526 | int chp_ssd_get_mask(struct chsc_ssd_info *ssd, struct chp_link *link) | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 527 | { | 
 | 528 | 	int i; | 
 | 529 | 	int mask; | 
 | 530 |  | 
 | 531 | 	for (i = 0; i < 8; i++) { | 
 | 532 | 		mask = 0x80 >> i; | 
 | 533 | 		if (!(ssd->path_mask & mask)) | 
 | 534 | 			continue; | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 535 | 		if (!chp_id_is_equal(&ssd->chpid[i], &link->chpid)) | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 536 | 			continue; | 
 | 537 | 		if ((ssd->fla_valid_mask & mask) && | 
| Cornelia Huck | 99611f8 | 2008-07-14 09:59:02 +0200 | [diff] [blame] | 538 | 		    ((ssd->fla[i] & link->fla_mask) != link->fla)) | 
| Cornelia Huck | c820de3 | 2008-07-14 09:58:45 +0200 | [diff] [blame] | 539 | 			continue; | 
 | 540 | 		return mask; | 
 | 541 | 	} | 
 | 542 | 	return 0; | 
 | 543 | } | 
 | 544 | EXPORT_SYMBOL_GPL(chp_ssd_get_mask); | 
 | 545 |  | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 546 | static inline int info_bit_num(struct chp_id id) | 
 | 547 | { | 
 | 548 | 	return id.id + id.cssid * (__MAX_CHPID + 1); | 
 | 549 | } | 
 | 550 |  | 
 | 551 | /* Force chp_info refresh on next call to info_validate(). */ | 
 | 552 | static void info_expire(void) | 
 | 553 | { | 
 | 554 | 	mutex_lock(&info_lock); | 
 | 555 | 	chp_info_expires = jiffies - 1; | 
 | 556 | 	mutex_unlock(&info_lock); | 
 | 557 | } | 
 | 558 |  | 
 | 559 | /* Ensure that chp_info is up-to-date. */ | 
 | 560 | static int info_update(void) | 
 | 561 | { | 
 | 562 | 	int rc; | 
 | 563 |  | 
 | 564 | 	mutex_lock(&info_lock); | 
 | 565 | 	rc = 0; | 
 | 566 | 	if (time_after(jiffies, chp_info_expires)) { | 
 | 567 | 		/* Data is too old, update. */ | 
 | 568 | 		rc = sclp_chp_read_info(&chp_info); | 
 | 569 | 		chp_info_expires = jiffies + CHP_INFO_UPDATE_INTERVAL ; | 
 | 570 | 	} | 
 | 571 | 	mutex_unlock(&info_lock); | 
 | 572 |  | 
 | 573 | 	return rc; | 
 | 574 | } | 
 | 575 |  | 
 | 576 | /** | 
 | 577 |  * chp_info_get_status - retrieve configure status of a channel-path | 
 | 578 |  * @chpid: channel-path ID | 
 | 579 |  * | 
 | 580 |  * On success, return 0 for standby, 1 for configured, 2 for reserved, | 
 | 581 |  * 3 for not recognized. Return negative error code on error. | 
 | 582 |  */ | 
 | 583 | int chp_info_get_status(struct chp_id chpid) | 
 | 584 | { | 
 | 585 | 	int rc; | 
 | 586 | 	int bit; | 
 | 587 |  | 
 | 588 | 	rc = info_update(); | 
 | 589 | 	if (rc) | 
 | 590 | 		return rc; | 
 | 591 |  | 
 | 592 | 	bit = info_bit_num(chpid); | 
 | 593 | 	mutex_lock(&info_lock); | 
 | 594 | 	if (!chp_test_bit(chp_info.recognized, bit)) | 
 | 595 | 		rc = CHP_STATUS_NOT_RECOGNIZED; | 
 | 596 | 	else if (chp_test_bit(chp_info.configured, bit)) | 
 | 597 | 		rc = CHP_STATUS_CONFIGURED; | 
 | 598 | 	else if (chp_test_bit(chp_info.standby, bit)) | 
 | 599 | 		rc = CHP_STATUS_STANDBY; | 
 | 600 | 	else | 
 | 601 | 		rc = CHP_STATUS_RESERVED; | 
 | 602 | 	mutex_unlock(&info_lock); | 
 | 603 |  | 
 | 604 | 	return rc; | 
 | 605 | } | 
 | 606 |  | 
 | 607 | /* Return configure task for chpid. */ | 
 | 608 | static enum cfg_task_t cfg_get_task(struct chp_id chpid) | 
 | 609 | { | 
 | 610 | 	return chp_cfg_task[chpid.cssid][chpid.id]; | 
 | 611 | } | 
 | 612 |  | 
 | 613 | /* Set configure task for chpid. */ | 
 | 614 | static void cfg_set_task(struct chp_id chpid, enum cfg_task_t cfg) | 
 | 615 | { | 
 | 616 | 	chp_cfg_task[chpid.cssid][chpid.id] = cfg; | 
 | 617 | } | 
 | 618 |  | 
 | 619 | /* Perform one configure/deconfigure request. Reschedule work function until | 
 | 620 |  * last request. */ | 
 | 621 | static void cfg_func(struct work_struct *work) | 
 | 622 | { | 
 | 623 | 	struct chp_id chpid; | 
 | 624 | 	enum cfg_task_t t; | 
| Peter Oberparleiter | 683c541 | 2008-07-14 09:59:04 +0200 | [diff] [blame] | 625 | 	int rc; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 626 |  | 
 | 627 | 	mutex_lock(&cfg_lock); | 
 | 628 | 	t = cfg_none; | 
 | 629 | 	chp_id_for_each(&chpid) { | 
 | 630 | 		t = cfg_get_task(chpid); | 
 | 631 | 		if (t != cfg_none) { | 
 | 632 | 			cfg_set_task(chpid, cfg_none); | 
 | 633 | 			break; | 
 | 634 | 		} | 
 | 635 | 	} | 
 | 636 | 	mutex_unlock(&cfg_lock); | 
 | 637 |  | 
 | 638 | 	switch (t) { | 
 | 639 | 	case cfg_configure: | 
| Peter Oberparleiter | 683c541 | 2008-07-14 09:59:04 +0200 | [diff] [blame] | 640 | 		rc = sclp_chp_configure(chpid); | 
 | 641 | 		if (rc) | 
 | 642 | 			CIO_MSG_EVENT(2, "chp: sclp_chp_configure(%x.%02x)=" | 
 | 643 | 				      "%d\n", chpid.cssid, chpid.id, rc); | 
 | 644 | 		else { | 
 | 645 | 			info_expire(); | 
 | 646 | 			chsc_chp_online(chpid); | 
 | 647 | 		} | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 648 | 		break; | 
 | 649 | 	case cfg_deconfigure: | 
| Peter Oberparleiter | 683c541 | 2008-07-14 09:59:04 +0200 | [diff] [blame] | 650 | 		rc = sclp_chp_deconfigure(chpid); | 
 | 651 | 		if (rc) | 
 | 652 | 			CIO_MSG_EVENT(2, "chp: sclp_chp_deconfigure(%x.%02x)=" | 
 | 653 | 				      "%d\n", chpid.cssid, chpid.id, rc); | 
 | 654 | 		else { | 
 | 655 | 			info_expire(); | 
 | 656 | 			chsc_chp_offline(chpid); | 
 | 657 | 		} | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 658 | 		break; | 
 | 659 | 	case cfg_none: | 
 | 660 | 		/* Get updated information after last change. */ | 
 | 661 | 		info_update(); | 
 | 662 | 		mutex_lock(&cfg_lock); | 
 | 663 | 		cfg_busy = 0; | 
 | 664 | 		mutex_unlock(&cfg_lock); | 
 | 665 | 		wake_up_interruptible(&cfg_wait_queue); | 
 | 666 | 		return; | 
 | 667 | 	} | 
 | 668 | 	queue_work(chp_wq, &cfg_work); | 
 | 669 | } | 
 | 670 |  | 
 | 671 | /** | 
 | 672 |  * chp_cfg_schedule - schedule chpid configuration request | 
 | 673 |  * @chpid - channel-path ID | 
 | 674 |  * @configure - Non-zero for configure, zero for deconfigure | 
 | 675 |  * | 
 | 676 |  * Schedule a channel-path configuration/deconfiguration request. | 
 | 677 |  */ | 
 | 678 | void chp_cfg_schedule(struct chp_id chpid, int configure) | 
 | 679 | { | 
 | 680 | 	CIO_MSG_EVENT(2, "chp_cfg_sched%x.%02x=%d\n", chpid.cssid, chpid.id, | 
 | 681 | 		      configure); | 
 | 682 | 	mutex_lock(&cfg_lock); | 
 | 683 | 	cfg_set_task(chpid, configure ? cfg_configure : cfg_deconfigure); | 
 | 684 | 	cfg_busy = 1; | 
 | 685 | 	mutex_unlock(&cfg_lock); | 
 | 686 | 	queue_work(chp_wq, &cfg_work); | 
 | 687 | } | 
 | 688 |  | 
 | 689 | /** | 
 | 690 |  * chp_cfg_cancel_deconfigure - cancel chpid deconfiguration request | 
 | 691 |  * @chpid - channel-path ID | 
 | 692 |  * | 
 | 693 |  * Cancel an active channel-path deconfiguration request if it has not yet | 
 | 694 |  * been performed. | 
 | 695 |  */ | 
 | 696 | void chp_cfg_cancel_deconfigure(struct chp_id chpid) | 
 | 697 | { | 
 | 698 | 	CIO_MSG_EVENT(2, "chp_cfg_cancel:%x.%02x\n", chpid.cssid, chpid.id); | 
 | 699 | 	mutex_lock(&cfg_lock); | 
 | 700 | 	if (cfg_get_task(chpid) == cfg_deconfigure) | 
 | 701 | 		cfg_set_task(chpid, cfg_none); | 
 | 702 | 	mutex_unlock(&cfg_lock); | 
 | 703 | } | 
 | 704 |  | 
 | 705 | static int cfg_wait_idle(void) | 
 | 706 | { | 
 | 707 | 	if (wait_event_interruptible(cfg_wait_queue, !cfg_busy)) | 
 | 708 | 		return -ERESTARTSYS; | 
 | 709 | 	return 0; | 
 | 710 | } | 
 | 711 |  | 
 | 712 | static int __init chp_init(void) | 
 | 713 | { | 
 | 714 | 	struct chp_id chpid; | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 715 | 	int ret; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 716 |  | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 717 | 	ret = crw_register_handler(CRW_RSC_CPATH, chp_process_crw); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 718 | 	if (ret) | 
 | 719 | 		return ret; | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 720 | 	chp_wq = create_singlethread_workqueue("cio_chp"); | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 721 | 	if (!chp_wq) { | 
| Heiko Carstens | f5daba1 | 2009-03-26 15:24:01 +0100 | [diff] [blame] | 722 | 		crw_unregister_handler(CRW_RSC_CPATH); | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 723 | 		return -ENOMEM; | 
| Cornelia Huck | c115618 | 2008-07-14 09:58:46 +0200 | [diff] [blame] | 724 | 	} | 
| Peter Oberparleiter | e5854a5 | 2007-04-27 16:01:31 +0200 | [diff] [blame] | 725 | 	INIT_WORK(&cfg_work, cfg_func); | 
 | 726 | 	init_waitqueue_head(&cfg_wait_queue); | 
 | 727 | 	if (info_update()) | 
 | 728 | 		return 0; | 
 | 729 | 	/* Register available channel-paths. */ | 
 | 730 | 	chp_id_for_each(&chpid) { | 
 | 731 | 		if (chp_info_get_status(chpid) != CHP_STATUS_NOT_RECOGNIZED) | 
 | 732 | 			chp_new(chpid); | 
 | 733 | 	} | 
 | 734 |  | 
 | 735 | 	return 0; | 
 | 736 | } | 
 | 737 |  | 
 | 738 | subsys_initcall(chp_init); |