aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteffen Maier <maier@linux.vnet.ibm.com>2012-09-04 09:23:32 -0400
committerJames Bottomley <JBottomley@Parallels.com>2012-09-24 04:11:01 -0400
commitcb45214960bc989af8b911ebd77da541c797717d (patch)
treec6d934378945076e699eeeea5880a452cbf53552
parent01e60527f0a49b3d7df603010bd6079bb4b6cf07 (diff)
[SCSI] zfcp: Do not wakeup while suspended
If the mapping of FCP device bus ID and corresponding subchannel is modified while the Linux image is suspended, the resume of FCP devices can fail. During resume, zfcp gets callbacks from cio regarding the modified subchannels but they can be arbitrarily mixed with the restore/resume callback. Since the cio callbacks would trigger adapter recovery, zfcp could wakeup before the resume callback. Therefore, ignore the cio callbacks regarding subchannels while being suspended. We can safely do so, since zfcp does not deal itself with subchannels. For problem determination purposes, we still trace the ignored callback events. The following kernel messages could be seen on resume: kernel: <WWPN>: parent <FCP device bus ID> should not be sleeping As part of adapter reopen recovery, zfcp performs auto port scanning which can erroneously try to register new remote ports with scsi_transport_fc and the device core code complains about the parent (adapter) still sleeping. kernel: zfcp.3dff9c: <FCP device bus ID>:\ Setting up the QDIO connection to the FCP adapter failed <last kernel message repeated 3 more times> kernel: zfcp.574d43: <FCP device bus ID>:\ ERP cannot recover an error on the FCP device In such cases, the adapter gave up recovery and remained blocked along with its child objects: remote ports and LUNs/scsi devices. Even the adapter shutdown as part of giving up recovery failed because the ccw device state remained disconnected. Later, the corresponding remote ports ran into dev_loss_tmo. As a result, the LUNs were erroneously not available again after resume. Even a manually triggered adapter recovery (e.g. sysfs attribute failed, or device offline/online via sysfs) could not recover the adapter due to the remaining disconnected state of the corresponding ccw device. Signed-off-by: Steffen Maier <maier@linux.vnet.ibm.com> Cc: <stable@vger.kernel.org> #2.6.32+ Signed-off-by: James Bottomley <JBottomley@Parallels.com>
-rw-r--r--drivers/s390/scsi/zfcp_ccw.c73
-rw-r--r--drivers/s390/scsi/zfcp_dbf.c20
-rw-r--r--drivers/s390/scsi/zfcp_dbf.h1
-rw-r--r--drivers/s390/scsi/zfcp_def.h1
-rw-r--r--drivers/s390/scsi/zfcp_ext.h1
5 files changed, 86 insertions, 10 deletions
diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c
index e37f04551948..9646766360d3 100644
--- a/drivers/s390/scsi/zfcp_ccw.c
+++ b/drivers/s390/scsi/zfcp_ccw.c
@@ -39,17 +39,23 @@ void zfcp_ccw_adapter_put(struct zfcp_adapter *adapter)
39 spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags); 39 spin_unlock_irqrestore(&zfcp_ccw_adapter_ref_lock, flags);
40} 40}
41 41
42static int zfcp_ccw_activate(struct ccw_device *cdev) 42/**
43 43 * zfcp_ccw_activate - activate adapter and wait for it to finish
44 * @cdev: pointer to belonging ccw device
45 * @clear: Status flags to clear.
46 * @tag: s390dbf trace record tag
47 */
48static int zfcp_ccw_activate(struct ccw_device *cdev, int clear, char *tag)
44{ 49{
45 struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); 50 struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
46 51
47 if (!adapter) 52 if (!adapter)
48 return 0; 53 return 0;
49 54
55 zfcp_erp_clear_adapter_status(adapter, clear);
50 zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING); 56 zfcp_erp_set_adapter_status(adapter, ZFCP_STATUS_COMMON_RUNNING);
51 zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 57 zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED,
52 "ccresu2"); 58 tag);
53 zfcp_erp_wait(adapter); 59 zfcp_erp_wait(adapter);
54 flush_work(&adapter->scan_work); 60 flush_work(&adapter->scan_work);
55 61
@@ -164,26 +170,29 @@ static int zfcp_ccw_set_online(struct ccw_device *cdev)
164 BUG_ON(!zfcp_reqlist_isempty(adapter->req_list)); 170 BUG_ON(!zfcp_reqlist_isempty(adapter->req_list));
165 adapter->req_no = 0; 171 adapter->req_no = 0;
166 172
167 zfcp_ccw_activate(cdev); 173 zfcp_ccw_activate(cdev, 0, "ccsonl1");
168 zfcp_ccw_adapter_put(adapter); 174 zfcp_ccw_adapter_put(adapter);
169 return 0; 175 return 0;
170} 176}
171 177
172/** 178/**
173 * zfcp_ccw_set_offline - set_offline function of zfcp driver 179 * zfcp_ccw_offline_sync - shut down adapter and wait for it to finish
174 * @cdev: pointer to belonging ccw device 180 * @cdev: pointer to belonging ccw device
181 * @set: Status flags to set.
182 * @tag: s390dbf trace record tag
175 * 183 *
176 * This function gets called by the common i/o layer and sets an adapter 184 * This function gets called by the common i/o layer and sets an adapter
177 * into state offline. 185 * into state offline.
178 */ 186 */
179static int zfcp_ccw_set_offline(struct ccw_device *cdev) 187static int zfcp_ccw_offline_sync(struct ccw_device *cdev, int set, char *tag)
180{ 188{
181 struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev); 189 struct zfcp_adapter *adapter = zfcp_ccw_adapter_by_cdev(cdev);
182 190
183 if (!adapter) 191 if (!adapter)
184 return 0; 192 return 0;
185 193
186 zfcp_erp_adapter_shutdown(adapter, 0, "ccsoff1"); 194 zfcp_erp_set_adapter_status(adapter, set);
195 zfcp_erp_adapter_shutdown(adapter, 0, tag);
187 zfcp_erp_wait(adapter); 196 zfcp_erp_wait(adapter);
188 197
189 zfcp_ccw_adapter_put(adapter); 198 zfcp_ccw_adapter_put(adapter);
@@ -191,6 +200,18 @@ static int zfcp_ccw_set_offline(struct ccw_device *cdev)
191} 200}
192 201
193/** 202/**
203 * zfcp_ccw_set_offline - set_offline function of zfcp driver
204 * @cdev: pointer to belonging ccw device
205 *
206 * This function gets called by the common i/o layer and sets an adapter
207 * into state offline.
208 */
209static int zfcp_ccw_set_offline(struct ccw_device *cdev)
210{
211 return zfcp_ccw_offline_sync(cdev, 0, "ccsoff1");
212}
213
214/**
194 * zfcp_ccw_notify - ccw notify function 215 * zfcp_ccw_notify - ccw notify function
195 * @cdev: pointer to belonging ccw device 216 * @cdev: pointer to belonging ccw device
196 * @event: indicates if adapter was detached or attached 217 * @event: indicates if adapter was detached or attached
@@ -207,6 +228,11 @@ static int zfcp_ccw_notify(struct ccw_device *cdev, int event)
207 228
208 switch (event) { 229 switch (event) {
209 case CIO_GONE: 230 case CIO_GONE:
231 if (atomic_read(&adapter->status) &
232 ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */
233 zfcp_dbf_hba_basic("ccnigo1", adapter);
234 break;
235 }
210 dev_warn(&cdev->dev, "The FCP device has been detached\n"); 236 dev_warn(&cdev->dev, "The FCP device has been detached\n");
211 zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1"); 237 zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti1");
212 break; 238 break;
@@ -216,6 +242,11 @@ static int zfcp_ccw_notify(struct ccw_device *cdev, int event)
216 zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2"); 242 zfcp_erp_adapter_shutdown(adapter, 0, "ccnoti2");
217 break; 243 break;
218 case CIO_OPER: 244 case CIO_OPER:
245 if (atomic_read(&adapter->status) &
246 ZFCP_STATUS_ADAPTER_SUSPENDED) { /* notification ignore */
247 zfcp_dbf_hba_basic("ccniop1", adapter);
248 break;
249 }
219 dev_info(&cdev->dev, "The FCP device is operational again\n"); 250 dev_info(&cdev->dev, "The FCP device is operational again\n");
220 zfcp_erp_set_adapter_status(adapter, 251 zfcp_erp_set_adapter_status(adapter,
221 ZFCP_STATUS_COMMON_RUNNING); 252 ZFCP_STATUS_COMMON_RUNNING);
@@ -251,6 +282,28 @@ static void zfcp_ccw_shutdown(struct ccw_device *cdev)
251 zfcp_ccw_adapter_put(adapter); 282 zfcp_ccw_adapter_put(adapter);
252} 283}
253 284
285static int zfcp_ccw_suspend(struct ccw_device *cdev)
286{
287 zfcp_ccw_offline_sync(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccsusp1");
288 return 0;
289}
290
291static int zfcp_ccw_thaw(struct ccw_device *cdev)
292{
293 /* trace records for thaw and final shutdown during suspend
294 can only be found in system dump until the end of suspend
295 but not after resume because it's based on the memory image
296 right after the very first suspend (freeze) callback */
297 zfcp_ccw_activate(cdev, 0, "ccthaw1");
298 return 0;
299}
300
301static int zfcp_ccw_resume(struct ccw_device *cdev)
302{
303 zfcp_ccw_activate(cdev, ZFCP_STATUS_ADAPTER_SUSPENDED, "ccresu1");
304 return 0;
305}
306
254struct ccw_driver zfcp_ccw_driver = { 307struct ccw_driver zfcp_ccw_driver = {
255 .driver = { 308 .driver = {
256 .owner = THIS_MODULE, 309 .owner = THIS_MODULE,
@@ -263,7 +316,7 @@ struct ccw_driver zfcp_ccw_driver = {
263 .set_offline = zfcp_ccw_set_offline, 316 .set_offline = zfcp_ccw_set_offline,
264 .notify = zfcp_ccw_notify, 317 .notify = zfcp_ccw_notify,
265 .shutdown = zfcp_ccw_shutdown, 318 .shutdown = zfcp_ccw_shutdown,
266 .freeze = zfcp_ccw_set_offline, 319 .freeze = zfcp_ccw_suspend,
267 .thaw = zfcp_ccw_activate, 320 .thaw = zfcp_ccw_thaw,
268 .restore = zfcp_ccw_activate, 321 .restore = zfcp_ccw_resume,
269}; 322};
diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c
index c6e47d553ad9..e1a8cc2526e7 100644
--- a/drivers/s390/scsi/zfcp_dbf.c
+++ b/drivers/s390/scsi/zfcp_dbf.c
@@ -200,6 +200,26 @@ void zfcp_dbf_hba_def_err(struct zfcp_adapter *adapter, u64 req_id, u16 scount,
200 spin_unlock_irqrestore(&dbf->pay_lock, flags); 200 spin_unlock_irqrestore(&dbf->pay_lock, flags);
201} 201}
202 202
203/**
204 * zfcp_dbf_hba_basic - trace event for basic adapter events
205 * @adapter: pointer to struct zfcp_adapter
206 */
207void zfcp_dbf_hba_basic(char *tag, struct zfcp_adapter *adapter)
208{
209 struct zfcp_dbf *dbf = adapter->dbf;
210 struct zfcp_dbf_hba *rec = &dbf->hba_buf;
211 unsigned long flags;
212
213 spin_lock_irqsave(&dbf->hba_lock, flags);
214 memset(rec, 0, sizeof(*rec));
215
216 memcpy(rec->tag, tag, ZFCP_DBF_TAG_LEN);
217 rec->id = ZFCP_DBF_HBA_BASIC;
218
219 debug_event(dbf->hba, 1, rec, sizeof(*rec));
220 spin_unlock_irqrestore(&dbf->hba_lock, flags);
221}
222
203static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec, 223static void zfcp_dbf_set_common(struct zfcp_dbf_rec *rec,
204 struct zfcp_adapter *adapter, 224 struct zfcp_adapter *adapter,
205 struct zfcp_port *port, 225 struct zfcp_port *port,
diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h
index 714f087eb7a9..3ac7a4b30dd9 100644
--- a/drivers/s390/scsi/zfcp_dbf.h
+++ b/drivers/s390/scsi/zfcp_dbf.h
@@ -154,6 +154,7 @@ enum zfcp_dbf_hba_id {
154 ZFCP_DBF_HBA_RES = 1, 154 ZFCP_DBF_HBA_RES = 1,
155 ZFCP_DBF_HBA_USS = 2, 155 ZFCP_DBF_HBA_USS = 2,
156 ZFCP_DBF_HBA_BIT = 3, 156 ZFCP_DBF_HBA_BIT = 3,
157 ZFCP_DBF_HBA_BASIC = 4,
157}; 158};
158 159
159/** 160/**
diff --git a/drivers/s390/scsi/zfcp_def.h b/drivers/s390/scsi/zfcp_def.h
index 2955e1a3deaf..5bc4afba8b09 100644
--- a/drivers/s390/scsi/zfcp_def.h
+++ b/drivers/s390/scsi/zfcp_def.h
@@ -77,6 +77,7 @@ struct zfcp_reqlist;
77#define ZFCP_STATUS_ADAPTER_SIOSL_ISSUED 0x00000004 77#define ZFCP_STATUS_ADAPTER_SIOSL_ISSUED 0x00000004
78#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008 78#define ZFCP_STATUS_ADAPTER_XCONFIG_OK 0x00000008
79#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010 79#define ZFCP_STATUS_ADAPTER_HOST_CON_INIT 0x00000010
80#define ZFCP_STATUS_ADAPTER_SUSPENDED 0x00000040
80#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100 81#define ZFCP_STATUS_ADAPTER_ERP_PENDING 0x00000100
81#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200 82#define ZFCP_STATUS_ADAPTER_LINK_UNPLUGGED 0x00000200
82#define ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED 0x00000400 83#define ZFCP_STATUS_ADAPTER_DATA_DIV_ENABLED 0x00000400
diff --git a/drivers/s390/scsi/zfcp_ext.h b/drivers/s390/scsi/zfcp_ext.h
index 36f422770ff5..4eeef78447f2 100644
--- a/drivers/s390/scsi/zfcp_ext.h
+++ b/drivers/s390/scsi/zfcp_ext.h
@@ -54,6 +54,7 @@ extern void zfcp_dbf_hba_fsf_res(char *, struct zfcp_fsf_req *);
54extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *); 54extern void zfcp_dbf_hba_bit_err(char *, struct zfcp_fsf_req *);
55extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *); 55extern void zfcp_dbf_hba_berr(struct zfcp_dbf *, struct zfcp_fsf_req *);
56extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **); 56extern void zfcp_dbf_hba_def_err(struct zfcp_adapter *, u64, u16, void **);
57extern void zfcp_dbf_hba_basic(char *, struct zfcp_adapter *);
57extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32); 58extern void zfcp_dbf_san_req(char *, struct zfcp_fsf_req *, u32);
58extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *); 59extern void zfcp_dbf_san_res(char *, struct zfcp_fsf_req *);
59extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *); 60extern void zfcp_dbf_san_in_els(char *, struct zfcp_fsf_req *);