diff options
| -rw-r--r-- | drivers/s390/cio/css.c | 203 |
1 files changed, 104 insertions, 99 deletions
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 13eeea3d547f..7086a74e9871 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c | |||
| @@ -182,136 +182,141 @@ get_subchannel_by_schid(struct subchannel_id schid) | |||
| 182 | return dev ? to_subchannel(dev) : NULL; | 182 | return dev ? to_subchannel(dev) : NULL; |
| 183 | } | 183 | } |
| 184 | 184 | ||
| 185 | 185 | static inline int css_get_subchannel_status(struct subchannel *sch) | |
| 186 | static inline int | ||
| 187 | css_get_subchannel_status(struct subchannel *sch, struct subchannel_id schid) | ||
| 188 | { | 186 | { |
| 189 | struct schib schib; | 187 | struct schib schib; |
| 190 | int cc; | ||
| 191 | 188 | ||
| 192 | cc = stsch(schid, &schib); | 189 | if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) |
| 193 | if (cc) | ||
| 194 | return CIO_GONE; | ||
| 195 | if (!schib.pmcw.dnv) | ||
| 196 | return CIO_GONE; | 190 | return CIO_GONE; |
| 197 | if (sch && sch->schib.pmcw.dnv && | 191 | if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) |
| 198 | (schib.pmcw.dev != sch->schib.pmcw.dev)) | ||
| 199 | return CIO_REVALIDATE; | 192 | return CIO_REVALIDATE; |
| 200 | if (sch && !sch->lpm) | 193 | if (!sch->lpm) |
| 201 | return CIO_NO_PATH; | 194 | return CIO_NO_PATH; |
| 202 | return CIO_OPER; | 195 | return CIO_OPER; |
| 203 | } | 196 | } |
| 204 | 197 | ||
| 205 | static int | 198 | static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) |
| 206 | css_evaluate_subchannel(struct subchannel_id schid, int slow) | ||
| 207 | { | 199 | { |
| 208 | int event, ret, disc; | 200 | int event, ret, disc; |
| 209 | struct subchannel *sch; | ||
| 210 | unsigned long flags; | 201 | unsigned long flags; |
| 202 | enum { NONE, UNREGISTER, UNREGISTER_PROBE, REPROBE } action; | ||
| 211 | 203 | ||
| 212 | sch = get_subchannel_by_schid(schid); | 204 | spin_lock_irqsave(&sch->lock, flags); |
| 213 | disc = sch ? device_is_disconnected(sch) : 0; | 205 | disc = device_is_disconnected(sch); |
| 214 | if (disc && slow) { | 206 | if (disc && slow) { |
| 215 | if (sch) | 207 | /* Disconnected devices are evaluated directly only.*/ |
| 216 | put_device(&sch->dev); | 208 | spin_unlock_irqrestore(&sch->lock, flags); |
| 217 | return 0; /* Already processed. */ | 209 | return 0; |
| 218 | } | 210 | } |
| 219 | /* | 211 | /* No interrupt after machine check - kill pending timers. */ |
| 220 | * We've got a machine check, so running I/O won't get an interrupt. | 212 | device_kill_pending_timer(sch); |
| 221 | * Kill any pending timers. | ||
| 222 | */ | ||
| 223 | if (sch) | ||
| 224 | device_kill_pending_timer(sch); | ||
| 225 | if (!disc && !slow) { | 213 | if (!disc && !slow) { |
| 226 | if (sch) | 214 | /* Non-disconnected devices are evaluated on the slow path. */ |
| 227 | put_device(&sch->dev); | 215 | spin_unlock_irqrestore(&sch->lock, flags); |
| 228 | return -EAGAIN; /* Will be done on the slow path. */ | 216 | return -EAGAIN; |
| 229 | } | 217 | } |
| 230 | event = css_get_subchannel_status(sch, schid); | 218 | event = css_get_subchannel_status(sch); |
| 231 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", | 219 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, %s, %s path.\n", |
| 232 | schid.ssid, schid.sch_no, event, | 220 | sch->schid.ssid, sch->schid.sch_no, event, |
| 233 | sch?(disc?"disconnected":"normal"):"unknown", | 221 | disc ? "disconnected" : "normal", |
| 234 | slow?"slow":"fast"); | 222 | slow ? "slow" : "fast"); |
| 223 | /* Analyze subchannel status. */ | ||
| 224 | action = NONE; | ||
| 235 | switch (event) { | 225 | switch (event) { |
| 236 | case CIO_NO_PATH: | 226 | case CIO_NO_PATH: |
| 237 | case CIO_GONE: | 227 | if (disc) { |
| 238 | if (!sch) { | 228 | /* Check if paths have become available. */ |
| 239 | /* Never used this subchannel. Ignore. */ | 229 | action = REPROBE; |
| 240 | ret = 0; | ||
| 241 | break; | 230 | break; |
| 242 | } | 231 | } |
| 243 | if (disc && (event == CIO_NO_PATH)) { | 232 | /* fall through */ |
| 244 | /* | 233 | case CIO_GONE: |
| 245 | * Uargh, hack again. Because we don't get a machine | 234 | /* Prevent unwanted effects when opening lock. */ |
| 246 | * check on configure on, our path bookkeeping can | 235 | cio_disable_subchannel(sch); |
| 247 | * be out of date here (it's fine while we only do | 236 | device_set_disconnected(sch); |
| 248 | * logical varying or get chsc machine checks). We | 237 | /* Ask driver what to do with device. */ |
| 249 | * need to force reprobing or we might miss devices | 238 | action = UNREGISTER; |
| 250 | * coming operational again. It won't do harm in real | 239 | if (sch->driver && sch->driver->notify) { |
| 251 | * no path situations. | ||
| 252 | */ | ||
| 253 | spin_lock_irqsave(&sch->lock, flags); | ||
| 254 | device_trigger_reprobe(sch); | ||
| 255 | spin_unlock_irqrestore(&sch->lock, flags); | 240 | spin_unlock_irqrestore(&sch->lock, flags); |
| 256 | ret = 0; | 241 | ret = sch->driver->notify(&sch->dev, event); |
| 257 | break; | 242 | spin_lock_irqsave(&sch->lock, flags); |
| 258 | } | 243 | if (ret) |
| 259 | if (sch->driver && sch->driver->notify && | 244 | action = NONE; |
| 260 | sch->driver->notify(&sch->dev, event)) { | ||
| 261 | cio_disable_subchannel(sch); | ||
| 262 | device_set_disconnected(sch); | ||
| 263 | ret = 0; | ||
| 264 | break; | ||
| 265 | } | 245 | } |
| 266 | /* | ||
| 267 | * Unregister subchannel. | ||
| 268 | * The device will be killed automatically. | ||
| 269 | */ | ||
| 270 | cio_disable_subchannel(sch); | ||
| 271 | css_sch_device_unregister(sch); | ||
| 272 | /* Reset intparm to zeroes. */ | ||
| 273 | sch->schib.pmcw.intparm = 0; | ||
| 274 | cio_modify(sch); | ||
| 275 | put_device(&sch->dev); | ||
| 276 | ret = 0; | ||
| 277 | break; | 246 | break; |
| 278 | case CIO_REVALIDATE: | 247 | case CIO_REVALIDATE: |
| 279 | /* | 248 | /* Device will be removed, so no notify necessary. */ |
| 280 | * Revalidation machine check. Sick. | 249 | if (disc) |
| 281 | * We don't notify the driver since we have to throw the device | 250 | /* Reprobe because immediate unregister might block. */ |
| 282 | * away in any case. | 251 | action = REPROBE; |
| 283 | */ | 252 | else |
| 284 | if (!disc) { | 253 | action = UNREGISTER_PROBE; |
| 285 | css_sch_device_unregister(sch); | ||
| 286 | /* Reset intparm to zeroes. */ | ||
| 287 | sch->schib.pmcw.intparm = 0; | ||
| 288 | cio_modify(sch); | ||
| 289 | put_device(&sch->dev); | ||
| 290 | ret = css_probe_device(schid); | ||
| 291 | } else { | ||
| 292 | /* | ||
| 293 | * We can't immediately deregister the disconnected | ||
| 294 | * device since it might block. | ||
| 295 | */ | ||
| 296 | spin_lock_irqsave(&sch->lock, flags); | ||
| 297 | device_trigger_reprobe(sch); | ||
| 298 | spin_unlock_irqrestore(&sch->lock, flags); | ||
| 299 | ret = 0; | ||
| 300 | } | ||
| 301 | break; | 254 | break; |
| 302 | case CIO_OPER: | 255 | case CIO_OPER: |
| 303 | if (disc) { | 256 | if (disc) |
| 304 | spin_lock_irqsave(&sch->lock, flags); | ||
| 305 | /* Get device operational again. */ | 257 | /* Get device operational again. */ |
| 306 | device_trigger_reprobe(sch); | 258 | action = REPROBE; |
| 307 | spin_unlock_irqrestore(&sch->lock, flags); | 259 | break; |
| 308 | } | 260 | } |
| 309 | ret = sch ? 0 : css_probe_device(schid); | 261 | /* Perform action. */ |
| 262 | ret = 0; | ||
| 263 | switch (action) { | ||
| 264 | case UNREGISTER: | ||
| 265 | case UNREGISTER_PROBE: | ||
| 266 | /* Unregister device (will use subchannel lock). */ | ||
| 267 | spin_unlock_irqrestore(&sch->lock, flags); | ||
| 268 | css_sch_device_unregister(sch); | ||
| 269 | spin_lock_irqsave(&sch->lock, flags); | ||
| 270 | |||
| 271 | /* Reset intparm to zeroes. */ | ||
| 272 | sch->schib.pmcw.intparm = 0; | ||
| 273 | cio_modify(sch); | ||
| 274 | |||
| 275 | /* Probe if necessary. */ | ||
| 276 | if (action == UNREGISTER_PROBE) | ||
| 277 | ret = css_probe_device(sch->schid); | ||
| 278 | break; | ||
| 279 | case REPROBE: | ||
| 280 | device_trigger_reprobe(sch); | ||
| 310 | break; | 281 | break; |
| 311 | default: | 282 | default: |
| 312 | BUG(); | 283 | break; |
| 313 | ret = 0; | 284 | } |
| 285 | spin_unlock_irqrestore(&sch->lock, flags); | ||
| 286 | |||
| 287 | return ret; | ||
| 288 | } | ||
| 289 | |||
| 290 | static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) | ||
| 291 | { | ||
| 292 | struct schib schib; | ||
| 293 | |||
| 294 | if (!slow) { | ||
| 295 | /* Will be done on the slow path. */ | ||
| 296 | return -EAGAIN; | ||
| 314 | } | 297 | } |
| 298 | if (stsch(schid, &schib) || !schib.pmcw.dnv) { | ||
| 299 | /* Unusable - ignore. */ | ||
| 300 | return 0; | ||
| 301 | } | ||
| 302 | CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " | ||
| 303 | "slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); | ||
| 304 | |||
| 305 | return css_probe_device(schid); | ||
| 306 | } | ||
| 307 | |||
| 308 | static int css_evaluate_subchannel(struct subchannel_id schid, int slow) | ||
| 309 | { | ||
| 310 | struct subchannel *sch; | ||
| 311 | int ret; | ||
| 312 | |||
| 313 | sch = get_subchannel_by_schid(schid); | ||
| 314 | if (sch) { | ||
| 315 | ret = css_evaluate_known_subchannel(sch, slow); | ||
| 316 | put_device(&sch->dev); | ||
| 317 | } else | ||
| 318 | ret = css_evaluate_new_subchannel(schid, slow); | ||
| 319 | |||
| 315 | return ret; | 320 | return ret; |
| 316 | } | 321 | } |
| 317 | 322 | ||
