]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Gerald Schaefer <geraldsc@de.ibm.com> |
2 | Subject: qdio: fix qeth port count detection. | |
3 | References: bnc#445100 | |
4 | ||
5 | Symptom: qeth network interface with multiple ports fails to initialize. | |
6 | Problem: qeth needs to get the port count information before | |
7 | qdio has allocated a page for the chsc operation. | |
8 | Otherwise the number of available ports could not be detected. | |
9 | Solution: Extend qdio_get_ssqd_desc() to store the data in the | |
10 | specified structure. | |
11 | ||
12 | Acked-by: John Jolly <jjolly@suse.de> | |
13 | ||
14 | --- | |
15 | arch/s390/include/asm/qdio.h | 16 ++++++++-------- | |
16 | drivers/s390/cio/qdio.h | 3 +++ | |
17 | drivers/s390/cio/qdio_main.c | 18 +++++++++--------- | |
18 | drivers/s390/cio/qdio_setup.c | 33 ++++++++++++++++++++++++--------- | |
19 | drivers/s390/net/qeth_core_main.c | 15 +++++++++++---- | |
20 | 5 files changed, 55 insertions(+), 30 deletions(-) | |
21 | ||
22 | Index: linux-sles11/arch/s390/include/asm/qdio.h | |
23 | =================================================================== | |
24 | --- linux-sles11.orig/arch/s390/include/asm/qdio.h | |
25 | +++ linux-sles11/arch/s390/include/asm/qdio.h | |
26 | @@ -367,16 +367,16 @@ struct qdio_initialize { | |
27 | #define QDIO_FLAG_SYNC_OUTPUT 0x02 | |
28 | #define QDIO_FLAG_PCI_OUT 0x10 | |
29 | ||
30 | -extern int qdio_initialize(struct qdio_initialize *init_data); | |
31 | -extern int qdio_allocate(struct qdio_initialize *init_data); | |
32 | -extern int qdio_establish(struct qdio_initialize *init_data); | |
33 | +extern int qdio_initialize(struct qdio_initialize *); | |
34 | +extern int qdio_allocate(struct qdio_initialize *); | |
35 | +extern int qdio_establish(struct qdio_initialize *); | |
36 | extern int qdio_activate(struct ccw_device *); | |
37 | ||
38 | -extern int do_QDIO(struct ccw_device*, unsigned int flags, | |
39 | - int q_nr, int qidx, int count); | |
40 | -extern int qdio_cleanup(struct ccw_device*, int how); | |
41 | -extern int qdio_shutdown(struct ccw_device*, int how); | |
42 | +extern int do_QDIO(struct ccw_device *cdev, unsigned int callflags, | |
43 | + int q_nr, int bufnr, int count); | |
44 | +extern int qdio_cleanup(struct ccw_device*, int); | |
45 | +extern int qdio_shutdown(struct ccw_device*, int); | |
46 | extern int qdio_free(struct ccw_device *); | |
47 | -extern struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev); | |
48 | +extern int qdio_get_ssqd_desc(struct ccw_device *dev, struct qdio_ssqd_desc*); | |
49 | ||
50 | #endif /* __QDIO_H__ */ | |
51 | Index: linux-sles11/drivers/s390/cio/qdio.h | |
52 | =================================================================== | |
53 | --- linux-sles11.orig/drivers/s390/cio/qdio.h | |
54 | +++ linux-sles11/drivers/s390/cio/qdio.h | |
55 | @@ -375,6 +375,9 @@ void qdio_int_handler(struct ccw_device | |
56 | int qdio_allocate_qs(struct qdio_irq *irq_ptr, int nr_input_qs, | |
57 | int nr_output_qs); | |
58 | void qdio_setup_ssqd_info(struct qdio_irq *irq_ptr); | |
59 | +int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, | |
60 | + struct subchannel_id *schid, | |
61 | + struct qdio_ssqd_desc *data); | |
62 | int qdio_setup_irq(struct qdio_initialize *init_data); | |
63 | void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, | |
64 | struct ccw_device *cdev); | |
65 | Index: linux-sles11/drivers/s390/cio/qdio_main.c | |
66 | =================================================================== | |
67 | --- linux-sles11.orig/drivers/s390/cio/qdio_main.c | |
68 | +++ linux-sles11/drivers/s390/cio/qdio_main.c | |
69 | @@ -1126,23 +1126,23 @@ void qdio_int_handler(struct ccw_device | |
70 | /** | |
71 | * qdio_get_ssqd_desc - get qdio subchannel description | |
72 | * @cdev: ccw device to get description for | |
73 | + * @data: where to store the ssqd | |
74 | * | |
75 | - * Returns a pointer to the saved qdio subchannel description, | |
76 | - * or NULL for not setup qdio devices. | |
77 | + * Returns 0 or an error code. The results of the chsc are stored in the | |
78 | + * specified structure. | |
79 | */ | |
80 | -struct qdio_ssqd_desc *qdio_get_ssqd_desc(struct ccw_device *cdev) | |
81 | +int qdio_get_ssqd_desc(struct ccw_device *cdev, | |
82 | + struct qdio_ssqd_desc *data) | |
83 | { | |
84 | - struct qdio_irq *irq_ptr; | |
85 | char dbf_text[15]; | |
86 | ||
87 | + if (!cdev || !cdev->private) | |
88 | + return -EINVAL; | |
89 | + | |
90 | sprintf(dbf_text, "qssq%4x", cdev->private->schid.sch_no); | |
91 | QDIO_DBF_TEXT0(0, setup, dbf_text); | |
92 | ||
93 | - irq_ptr = cdev->private->qdio_data; | |
94 | - if (!irq_ptr) | |
95 | - return NULL; | |
96 | - | |
97 | - return &irq_ptr->ssqd_desc; | |
98 | + return qdio_setup_get_ssqd(NULL, &cdev->private->schid, data); | |
99 | } | |
100 | EXPORT_SYMBOL_GPL(qdio_get_ssqd_desc); | |
101 | ||
102 | Index: linux-sles11/drivers/s390/cio/qdio_setup.c | |
103 | =================================================================== | |
104 | --- linux-sles11.orig/drivers/s390/cio/qdio_setup.c | |
105 | +++ linux-sles11/drivers/s390/cio/qdio_setup.c | |
106 | @@ -243,22 +243,31 @@ no_qebsm: | |
107 | QDIO_DBF_TEXT0(0, setup, "noV=V"); | |
108 | } | |
109 | ||
110 | -static int __get_ssqd_info(struct qdio_irq *irq_ptr) | |
111 | +/* | |
112 | + * If there is a qdio_irq we use the chsc_page and store the information | |
113 | + * in the qdio_irq, otherwise we copy it to the specified structure. | |
114 | + */ | |
115 | +int qdio_setup_get_ssqd(struct qdio_irq *irq_ptr, | |
116 | + struct subchannel_id *schid, | |
117 | + struct qdio_ssqd_desc *data) | |
118 | { | |
119 | struct chsc_ssqd_area *ssqd; | |
120 | int rc; | |
121 | ||
122 | QDIO_DBF_TEXT0(0, setup, "getssqd"); | |
123 | - ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; | |
124 | + if (irq_ptr != NULL) | |
125 | + ssqd = (struct chsc_ssqd_area *)irq_ptr->chsc_page; | |
126 | + else | |
127 | + ssqd = (struct chsc_ssqd_area *)__get_free_page(GFP_KERNEL); | |
128 | memset(ssqd, 0, PAGE_SIZE); | |
129 | ||
130 | ssqd->request = (struct chsc_header) { | |
131 | .length = 0x0010, | |
132 | .code = 0x0024, | |
133 | }; | |
134 | - ssqd->first_sch = irq_ptr->schid.sch_no; | |
135 | - ssqd->last_sch = irq_ptr->schid.sch_no; | |
136 | - ssqd->ssid = irq_ptr->schid.ssid; | |
137 | + ssqd->first_sch = schid->sch_no; | |
138 | + ssqd->last_sch = schid->sch_no; | |
139 | + ssqd->ssid = schid->ssid; | |
140 | ||
141 | if (chsc(ssqd)) | |
142 | return -EIO; | |
143 | @@ -268,11 +277,17 @@ static int __get_ssqd_info(struct qdio_i | |
144 | ||
145 | if (!(ssqd->qdio_ssqd.flags & CHSC_FLAG_QDIO_CAPABILITY) || | |
146 | !(ssqd->qdio_ssqd.flags & CHSC_FLAG_VALIDITY) || | |
147 | - (ssqd->qdio_ssqd.sch != irq_ptr->schid.sch_no)) | |
148 | + (ssqd->qdio_ssqd.sch != schid->sch_no)) | |
149 | return -EINVAL; | |
150 | ||
151 | - memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, | |
152 | - sizeof(struct qdio_ssqd_desc)); | |
153 | + if (irq_ptr != NULL) | |
154 | + memcpy(&irq_ptr->ssqd_desc, &ssqd->qdio_ssqd, | |
155 | + sizeof(struct qdio_ssqd_desc)); | |
156 | + else { | |
157 | + memcpy(&data, &ssqd->qdio_ssqd, | |
158 | + sizeof(struct qdio_ssqd_desc)); | |
159 | + free_page((unsigned long)ssqd); | |
160 | + } | |
161 | return 0; | |
162 | } | |
163 | ||
164 | @@ -282,7 +297,7 @@ void qdio_setup_ssqd_info(struct qdio_ir | |
165 | char dbf_text[15]; | |
166 | int rc; | |
167 | ||
168 | - rc = __get_ssqd_info(irq_ptr); | |
169 | + rc = qdio_setup_get_ssqd(irq_ptr, &irq_ptr->schid, NULL); | |
170 | if (rc) { | |
171 | QDIO_DBF_TEXT2(0, setup, "ssqdasig"); | |
172 | sprintf(dbf_text, "schn%4x", irq_ptr->schid.sch_no); | |
173 | Index: linux-sles11/drivers/s390/net/qeth_core_main.c | |
174 | =================================================================== | |
175 | --- linux-sles11.orig/drivers/s390/net/qeth_core_main.c | |
176 | +++ linux-sles11/drivers/s390/net/qeth_core_main.c | |
177 | @@ -3767,7 +3767,7 @@ static int qeth_core_driver_group(const | |
178 | ||
179 | int qeth_core_hardsetup_card(struct qeth_card *card) | |
180 | { | |
181 | - struct qdio_ssqd_desc *qdio_ssqd; | |
182 | + struct qdio_ssqd_desc *ssqd; | |
183 | int retries = 3; | |
184 | int mpno = 0; | |
185 | int rc; | |
186 | @@ -3803,9 +3803,16 @@ retry: | |
187 | return rc; | |
188 | } | |
189 | ||
190 | - qdio_ssqd = qdio_get_ssqd_desc(CARD_DDEV(card)); | |
191 | - if (qdio_ssqd) | |
192 | - mpno = qdio_ssqd->pcnt; | |
193 | + ssqd = kmalloc(sizeof(struct qdio_ssqd_desc), GFP_KERNEL); | |
194 | + if (!ssqd) { | |
195 | + rc = -ENOMEM; | |
196 | + goto out; | |
197 | + } | |
198 | + rc = qdio_get_ssqd_desc(CARD_DDEV(card), ssqd); | |
199 | + if (rc == 0) | |
200 | + mpno = ssqd->pcnt; | |
201 | + kfree(ssqd); | |
202 | + | |
203 | if (mpno) | |
204 | mpno = min(mpno - 1, QETH_MAX_PORTNO); | |
205 | if (card->info.portno > mpno) { |