瀏覽代碼

[S390] sclp: invalid handling of temporary 'not operational' status

Requests are aborted when the sclp interface reports 'not operational'
even though they may still be active at the sclp, leading to concurrent
writes to request memory by both the kernel and the sclp interface.
Do not abort requests for which the sclp interface reports not
operational status during request retry.

Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>5A
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Peter Oberparleiter 18 年之前
父節點
當前提交
dbd8ae6306
共有 1 個文件被更改,包括 49 次插入21 次删除
  1. 49 21
      drivers/s390/char/sclp.c

+ 49 - 21
drivers/s390/char/sclp.c

@@ -59,7 +59,8 @@ static volatile enum sclp_init_state_t {
 /* Internal state: is a request active at the sclp? */
 /* Internal state: is a request active at the sclp? */
 static volatile enum sclp_running_state_t {
 static volatile enum sclp_running_state_t {
 	sclp_running_state_idle,
 	sclp_running_state_idle,
-	sclp_running_state_running
+	sclp_running_state_running,
+	sclp_running_state_reset_pending
 } sclp_running_state = sclp_running_state_idle;
 } sclp_running_state = sclp_running_state_idle;
 
 
 /* Internal state: is a read request pending? */
 /* Internal state: is a read request pending? */
@@ -88,7 +89,7 @@ static volatile enum sclp_mask_state_t {
 
 
 /* Timeout intervals in seconds.*/
 /* Timeout intervals in seconds.*/
 #define SCLP_BUSY_INTERVAL	10
 #define SCLP_BUSY_INTERVAL	10
-#define SCLP_RETRY_INTERVAL	15
+#define SCLP_RETRY_INTERVAL	30
 
 
 static void sclp_process_queue(void);
 static void sclp_process_queue(void);
 static int sclp_init_mask(int calculate);
 static int sclp_init_mask(int calculate);
@@ -113,19 +114,17 @@ service_call(sclp_cmdw_t command, void *sccb)
 	return 0;
 	return 0;
 }
 }
 
 
-/* Request timeout handler. Restart the request queue. If DATA is non-zero,
- * force restart of running request. */
+static inline void __sclp_make_read_req(void);
+
 static void
 static void
-sclp_request_timeout(unsigned long data)
+__sclp_queue_read_req(void)
 {
 {
-	unsigned long flags;
-
-	if (data) {
-		spin_lock_irqsave(&sclp_lock, flags);
-		sclp_running_state = sclp_running_state_idle;
-		spin_unlock_irqrestore(&sclp_lock, flags);
+	if (sclp_reading_state == sclp_reading_state_idle) {
+		sclp_reading_state = sclp_reading_state_reading;
+		__sclp_make_read_req();
+		/* Add request to head of queue */
+		list_add(&sclp_read_req.list, &sclp_req_queue);
 	}
 	}
-	sclp_process_queue();
 }
 }
 
 
 /* Set up request retry timer. Called while sclp_lock is locked. */
 /* Set up request retry timer. Called while sclp_lock is locked. */
@@ -140,6 +139,29 @@ __sclp_set_request_timer(unsigned long time, void (*function)(unsigned long),
 	add_timer(&sclp_request_timer);
 	add_timer(&sclp_request_timer);
 }
 }
 
 
+/* Request timeout handler. Restart the request queue. If DATA is non-zero,
+ * force restart of running request. */
+static void
+sclp_request_timeout(unsigned long data)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sclp_lock, flags);
+	if (data) {
+		if (sclp_running_state == sclp_running_state_running) {
+			/* Break running state and queue NOP read event request
+			 * to get a defined interface state. */
+			__sclp_queue_read_req();
+			sclp_running_state = sclp_running_state_idle;
+		}
+	} else {
+		__sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ,
+					 sclp_request_timeout, 0);
+	}
+	spin_unlock_irqrestore(&sclp_lock, flags);
+	sclp_process_queue();
+}
+
 /* Try to start a request. Return zero if the request was successfully
 /* Try to start a request. Return zero if the request was successfully
  * started or if it will be started at a later time. Return non-zero otherwise.
  * started or if it will be started at a later time. Return non-zero otherwise.
  * Called while sclp_lock is locked. */
  * Called while sclp_lock is locked. */
@@ -191,7 +213,15 @@ sclp_process_queue(void)
 		rc = __sclp_start_request(req);
 		rc = __sclp_start_request(req);
 		if (rc == 0)
 		if (rc == 0)
 			break;
 			break;
-		/* Request failed. */
+		/* Request failed */
+		if (req->start_count > 1) {
+			/* Cannot abort already submitted request - could still
+			 * be active at the SCLP */
+			__sclp_set_request_timer(SCLP_BUSY_INTERVAL * HZ,
+						 sclp_request_timeout, 0);
+			break;
+		}
+		/* Post-processing for aborted request */
 		list_del(&req->list);
 		list_del(&req->list);
 		if (req->callback) {
 		if (req->callback) {
 			spin_unlock_irqrestore(&sclp_lock, flags);
 			spin_unlock_irqrestore(&sclp_lock, flags);
@@ -221,7 +251,8 @@ sclp_add_request(struct sclp_req *req)
 	list_add_tail(&req->list, &sclp_req_queue);
 	list_add_tail(&req->list, &sclp_req_queue);
 	rc = 0;
 	rc = 0;
 	/* Start if request is first in list */
 	/* Start if request is first in list */
-	if (req->list.prev == &sclp_req_queue) {
+	if (sclp_running_state == sclp_running_state_idle &&
+	    req->list.prev == &sclp_req_queue) {
 		rc = __sclp_start_request(req);
 		rc = __sclp_start_request(req);
 		if (rc)
 		if (rc)
 			list_del(&req->list);
 			list_del(&req->list);
@@ -334,6 +365,8 @@ sclp_interrupt_handler(__u16 code)
 	finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
 	finished_sccb = S390_lowcore.ext_params & 0xfffffff8;
 	evbuf_pending = S390_lowcore.ext_params & 0x3;
 	evbuf_pending = S390_lowcore.ext_params & 0x3;
 	if (finished_sccb) {
 	if (finished_sccb) {
+		del_timer(&sclp_request_timer);
+		sclp_running_state = sclp_running_state_reset_pending;
 		req = __sclp_find_req(finished_sccb);
 		req = __sclp_find_req(finished_sccb);
 		if (req) {
 		if (req) {
 			/* Request post-processing */
 			/* Request post-processing */
@@ -348,13 +381,8 @@ sclp_interrupt_handler(__u16 code)
 		sclp_running_state = sclp_running_state_idle;
 		sclp_running_state = sclp_running_state_idle;
 	}
 	}
 	if (evbuf_pending && sclp_receive_mask != 0 &&
 	if (evbuf_pending && sclp_receive_mask != 0 &&
-	    sclp_reading_state == sclp_reading_state_idle &&
-	    sclp_activation_state == sclp_activation_state_active ) {
-		sclp_reading_state = sclp_reading_state_reading;
-		__sclp_make_read_req();
-		/* Add request to head of queue */
-		list_add(&sclp_read_req.list, &sclp_req_queue);
-	}
+	    sclp_activation_state == sclp_activation_state_active)
+		__sclp_queue_read_req();
 	spin_unlock(&sclp_lock);
 	spin_unlock(&sclp_lock);
 	sclp_process_queue();
 	sclp_process_queue();
 }
 }