Commit 3cdb4559 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 's390-6.9-4' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Alexander Gordeev:

 - Fix NULL pointer dereference in program check handler

 - Fake IRBs are important events relevant for problem analysis. Add
   traces when queueing and delivering

 - Fix a race condition in ccw_device_set_online() that can cause the
   online process to fail

 - Deferred condition code 1 response indicates that I/O was not started
   and should be retried. The current QDIO implementation handles a cc1
   response as an error, resulting in a failed QDIO setup. Fix that by
   retrying the setup when a cc1 response is received

* tag 's390-6.9-4' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
  s390/mm: Fix NULL pointer dereference
  s390/cio: log fake IRB events
  s390/cio: fix race condition during online processing
  s390/qdio: handle deferred cc1
parents 9c6e84e4 d111855a
...@@ -340,7 +340,8 @@ SYM_CODE_START(pgm_check_handler) ...@@ -340,7 +340,8 @@ SYM_CODE_START(pgm_check_handler)
mvc __PT_LAST_BREAK(8,%r11),__LC_PGM_LAST_BREAK mvc __PT_LAST_BREAK(8,%r11),__LC_PGM_LAST_BREAK
stctg %c1,%c1,__PT_CR1(%r11) stctg %c1,%c1,__PT_CR1(%r11)
#if IS_ENABLED(CONFIG_KVM) #if IS_ENABLED(CONFIG_KVM)
lg %r12,__LC_GMAP ltg %r12,__LC_GMAP
jz 5f
clc __GMAP_ASCE(8,%r12), __PT_CR1(%r11) clc __GMAP_ASCE(8,%r12), __PT_CR1(%r11)
jne 5f jne 5f
BPENTER __SF_SIE_FLAGS(%r10),_TIF_ISOLATE_BP_GUEST BPENTER __SF_SIE_FLAGS(%r10),_TIF_ISOLATE_BP_GUEST
......
...@@ -363,10 +363,8 @@ int ccw_device_set_online(struct ccw_device *cdev) ...@@ -363,10 +363,8 @@ int ccw_device_set_online(struct ccw_device *cdev)
spin_lock_irq(cdev->ccwlock); spin_lock_irq(cdev->ccwlock);
ret = ccw_device_online(cdev); ret = ccw_device_online(cdev);
spin_unlock_irq(cdev->ccwlock); if (ret) {
if (ret == 0) spin_unlock_irq(cdev->ccwlock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
else {
CIO_MSG_EVENT(0, "ccw_device_online returned %d, " CIO_MSG_EVENT(0, "ccw_device_online returned %d, "
"device 0.%x.%04x\n", "device 0.%x.%04x\n",
ret, cdev->private->dev_id.ssid, ret, cdev->private->dev_id.ssid,
...@@ -375,7 +373,12 @@ int ccw_device_set_online(struct ccw_device *cdev) ...@@ -375,7 +373,12 @@ int ccw_device_set_online(struct ccw_device *cdev)
put_device(&cdev->dev); put_device(&cdev->dev);
return ret; return ret;
} }
spin_lock_irq(cdev->ccwlock); /* Wait until a final state is reached */
while (!dev_fsm_final_state(cdev)) {
spin_unlock_irq(cdev->ccwlock);
wait_event(cdev->private->wait_q, dev_fsm_final_state(cdev));
spin_lock_irq(cdev->ccwlock);
}
/* Check if online processing was successful */ /* Check if online processing was successful */
if ((cdev->private->state != DEV_STATE_ONLINE) && if ((cdev->private->state != DEV_STATE_ONLINE) &&
(cdev->private->state != DEV_STATE_W4SENSE)) { (cdev->private->state != DEV_STATE_W4SENSE)) {
......
...@@ -504,6 +504,11 @@ void ccw_device_verify_done(struct ccw_device *cdev, int err) ...@@ -504,6 +504,11 @@ void ccw_device_verify_done(struct ccw_device *cdev, int err)
ccw_device_done(cdev, DEV_STATE_ONLINE); ccw_device_done(cdev, DEV_STATE_ONLINE);
/* Deliver fake irb to device driver, if needed. */ /* Deliver fake irb to device driver, if needed. */
if (cdev->private->flags.fake_irb) { if (cdev->private->flags.fake_irb) {
CIO_MSG_EVENT(2, "fakeirb: deliver device 0.%x.%04x intparm %lx type=%d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno,
cdev->private->intparm,
cdev->private->flags.fake_irb);
create_fake_irb(&cdev->private->dma_area->irb, create_fake_irb(&cdev->private->dma_area->irb,
cdev->private->flags.fake_irb); cdev->private->flags.fake_irb);
cdev->private->flags.fake_irb = 0; cdev->private->flags.fake_irb = 0;
......
...@@ -208,6 +208,10 @@ int ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa, ...@@ -208,6 +208,10 @@ int ccw_device_start_timeout_key(struct ccw_device *cdev, struct ccw1 *cpa,
if (!cdev->private->flags.fake_irb) { if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = FAKE_CMD_IRB; cdev->private->flags.fake_irb = FAKE_CMD_IRB;
cdev->private->intparm = intparm; cdev->private->intparm = intparm;
CIO_MSG_EVENT(2, "fakeirb: queue device 0.%x.%04x intparm %lx type=%d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, intparm,
cdev->private->flags.fake_irb);
return 0; return 0;
} else } else
/* There's already a fake I/O around. */ /* There's already a fake I/O around. */
...@@ -551,6 +555,10 @@ int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw, ...@@ -551,6 +555,10 @@ int ccw_device_tm_start_timeout_key(struct ccw_device *cdev, struct tcw *tcw,
if (!cdev->private->flags.fake_irb) { if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = FAKE_TM_IRB; cdev->private->flags.fake_irb = FAKE_TM_IRB;
cdev->private->intparm = intparm; cdev->private->intparm = intparm;
CIO_MSG_EVENT(2, "fakeirb: queue device 0.%x.%04x intparm %lx type=%d\n",
cdev->private->dev_id.ssid,
cdev->private->dev_id.devno, intparm,
cdev->private->flags.fake_irb);
return 0; return 0;
} else } else
/* There's already a fake I/O around. */ /* There's already a fake I/O around. */
......
...@@ -722,8 +722,8 @@ static void qdio_handle_activate_check(struct qdio_irq *irq_ptr, ...@@ -722,8 +722,8 @@ static void qdio_handle_activate_check(struct qdio_irq *irq_ptr,
lgr_info_log(); lgr_info_log();
} }
static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, static int qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat,
int dstat) int dstat, int dcc)
{ {
DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq"); DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qest irq");
...@@ -731,15 +731,18 @@ static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat, ...@@ -731,15 +731,18 @@ static void qdio_establish_handle_irq(struct qdio_irq *irq_ptr, int cstat,
goto error; goto error;
if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END)) if (dstat & ~(DEV_STAT_DEV_END | DEV_STAT_CHN_END))
goto error; goto error;
if (dcc == 1)
return -EAGAIN;
if (!(dstat & DEV_STAT_DEV_END)) if (!(dstat & DEV_STAT_DEV_END))
goto error; goto error;
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ESTABLISHED);
return; return 0;
error: error:
DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no); DBF_ERROR("%4x EQ:error", irq_ptr->schid.sch_no);
DBF_ERROR("ds: %2x cs:%2x", dstat, cstat); DBF_ERROR("ds: %2x cs:%2x", dstat, cstat);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
return -EIO;
} }
/* qdio interrupt handler */ /* qdio interrupt handler */
...@@ -748,7 +751,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -748,7 +751,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
{ {
struct qdio_irq *irq_ptr = cdev->private->qdio_data; struct qdio_irq *irq_ptr = cdev->private->qdio_data;
struct subchannel_id schid; struct subchannel_id schid;
int cstat, dstat; int cstat, dstat, rc, dcc;
if (!intparm || !irq_ptr) { if (!intparm || !irq_ptr) {
ccw_device_get_schid(cdev, &schid); ccw_device_get_schid(cdev, &schid);
...@@ -768,10 +771,12 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -768,10 +771,12 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
qdio_irq_check_sense(irq_ptr, irb); qdio_irq_check_sense(irq_ptr, irb);
cstat = irb->scsw.cmd.cstat; cstat = irb->scsw.cmd.cstat;
dstat = irb->scsw.cmd.dstat; dstat = irb->scsw.cmd.dstat;
dcc = scsw_cmd_is_valid_cc(&irb->scsw) ? irb->scsw.cmd.cc : 0;
rc = 0;
switch (irq_ptr->state) { switch (irq_ptr->state) {
case QDIO_IRQ_STATE_INACTIVE: case QDIO_IRQ_STATE_INACTIVE:
qdio_establish_handle_irq(irq_ptr, cstat, dstat); rc = qdio_establish_handle_irq(irq_ptr, cstat, dstat, dcc);
break; break;
case QDIO_IRQ_STATE_CLEANUP: case QDIO_IRQ_STATE_CLEANUP:
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
...@@ -785,12 +790,25 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, ...@@ -785,12 +790,25 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
if (cstat || dstat) if (cstat || dstat)
qdio_handle_activate_check(irq_ptr, intparm, cstat, qdio_handle_activate_check(irq_ptr, intparm, cstat,
dstat); dstat);
else if (dcc == 1)
rc = -EAGAIN;
break; break;
case QDIO_IRQ_STATE_STOPPED: case QDIO_IRQ_STATE_STOPPED:
break; break;
default: default:
WARN_ON_ONCE(1); WARN_ON_ONCE(1);
} }
if (rc == -EAGAIN) {
DBF_DEV_EVENT(DBF_INFO, irq_ptr, "qint retry");
rc = ccw_device_start(cdev, irq_ptr->ccw, intparm, 0, 0);
if (!rc)
return;
DBF_ERROR("%4x RETRY ERR", irq_ptr->schid.sch_no);
DBF_ERROR("rc:%4x", rc);
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_ERR);
}
wake_up(&cdev->private->wait_q); wake_up(&cdev->private->wait_q);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment