diff options
Diffstat (limited to 'drivers/scsi/scsi_transport_iscsi.c')
-rw-r--r-- | drivers/scsi/scsi_transport_iscsi.c | 859 |
1 files changed, 448 insertions, 411 deletions
diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index e08462d50c97..59a1c9d9d3bd 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c | |||
@@ -21,11 +21,9 @@ | |||
21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | 21 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 | */ | 22 | */ |
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/string.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/mempool.h> | 24 | #include <linux/mempool.h> |
25 | #include <linux/mutex.h> | ||
27 | #include <net/tcp.h> | 26 | #include <net/tcp.h> |
28 | |||
29 | #include <scsi/scsi.h> | 27 | #include <scsi/scsi.h> |
30 | #include <scsi/scsi_host.h> | 28 | #include <scsi/scsi_host.h> |
31 | #include <scsi/scsi_device.h> | 29 | #include <scsi/scsi_device.h> |
@@ -45,11 +43,6 @@ struct iscsi_internal { | |||
45 | */ | 43 | */ |
46 | struct list_head sessions; | 44 | struct list_head sessions; |
47 | /* | 45 | /* |
48 | * lock to serialize access to the sessions list which must | ||
49 | * be taken after the rx_queue_sema | ||
50 | */ | ||
51 | spinlock_t session_lock; | ||
52 | /* | ||
53 | * based on transport capabilities, at register time we set these | 46 | * based on transport capabilities, at register time we set these |
54 | * bits to tell the transport class it wants attributes displayed | 47 | * bits to tell the transport class it wants attributes displayed |
55 | * in sysfs or that it can support different iSCSI Data-Path | 48 | * in sysfs or that it can support different iSCSI Data-Path |
@@ -70,7 +63,7 @@ struct iscsi_internal { | |||
70 | /* | 63 | /* |
71 | * list of registered transports and lock that must | 64 | * list of registered transports and lock that must |
72 | * be held while accessing list. The iscsi_transport_lock must | 65 | * be held while accessing list. The iscsi_transport_lock must |
73 | * be acquired after the rx_queue_sema. | 66 | * be acquired after the rx_queue_mutex. |
74 | */ | 67 | */ |
75 | static LIST_HEAD(iscsi_transports); | 68 | static LIST_HEAD(iscsi_transports); |
76 | static DEFINE_SPINLOCK(iscsi_transport_lock); | 69 | static DEFINE_SPINLOCK(iscsi_transport_lock); |
@@ -145,7 +138,7 @@ static DECLARE_TRANSPORT_CLASS(iscsi_connection_class, | |||
145 | 138 | ||
146 | static struct sock *nls; | 139 | static struct sock *nls; |
147 | static int daemon_pid; | 140 | static int daemon_pid; |
148 | static DECLARE_MUTEX(rx_queue_sema); | 141 | static DEFINE_MUTEX(rx_queue_mutex); |
149 | 142 | ||
150 | struct mempool_zone { | 143 | struct mempool_zone { |
151 | mempool_t *pool; | 144 | mempool_t *pool; |
@@ -156,7 +149,7 @@ struct mempool_zone { | |||
156 | spinlock_t freelock; | 149 | spinlock_t freelock; |
157 | }; | 150 | }; |
158 | 151 | ||
159 | static struct mempool_zone z_reply; | 152 | static struct mempool_zone *z_reply; |
160 | 153 | ||
161 | /* | 154 | /* |
162 | * Z_MAX_* - actual mempool size allocated at the mempool_zone_init() time | 155 | * Z_MAX_* - actual mempool size allocated at the mempool_zone_init() time |
@@ -171,50 +164,271 @@ static struct mempool_zone z_reply; | |||
171 | #define Z_MAX_ERROR 16 | 164 | #define Z_MAX_ERROR 16 |
172 | #define Z_HIWAT_ERROR 12 | 165 | #define Z_HIWAT_ERROR 12 |
173 | 166 | ||
174 | struct iscsi_if_conn { | 167 | static LIST_HEAD(connlist); |
175 | struct list_head conn_list; /* item in connlist */ | 168 | static DEFINE_SPINLOCK(connlock); |
176 | struct list_head session_list; /* item in session->connections */ | ||
177 | iscsi_connh_t connh; | ||
178 | int active; /* must be accessed with the connlock */ | ||
179 | struct Scsi_Host *host; /* originated shost */ | ||
180 | struct device dev; /* sysfs transport/container device */ | ||
181 | struct iscsi_transport *transport; | ||
182 | struct mempool_zone z_error; | ||
183 | struct mempool_zone z_pdu; | ||
184 | struct list_head freequeue; | ||
185 | }; | ||
186 | 169 | ||
187 | #define iscsi_dev_to_if_conn(_dev) \ | 170 | /* |
188 | container_of(_dev, struct iscsi_if_conn, dev) | 171 | * The following functions can be used by LLDs that allocate |
172 | * their own scsi_hosts or by software iscsi LLDs | ||
173 | */ | ||
174 | static void iscsi_session_release(struct device *dev) | ||
175 | { | ||
176 | struct iscsi_cls_session *session = iscsi_dev_to_session(dev); | ||
177 | struct iscsi_transport *transport = session->transport; | ||
178 | struct Scsi_Host *shost; | ||
189 | 179 | ||
190 | #define iscsi_cdev_to_if_conn(_cdev) \ | 180 | shost = iscsi_session_to_shost(session); |
191 | iscsi_dev_to_if_conn(_cdev->dev) | 181 | scsi_host_put(shost); |
182 | kfree(session); | ||
183 | module_put(transport->owner); | ||
184 | } | ||
192 | 185 | ||
193 | static LIST_HEAD(connlist); | 186 | static int iscsi_is_session_dev(const struct device *dev) |
194 | static DEFINE_SPINLOCK(connlock); | 187 | { |
188 | return dev->release == iscsi_session_release; | ||
189 | } | ||
195 | 190 | ||
196 | struct iscsi_if_session { | 191 | /** |
197 | struct list_head list; /* item in session_list */ | 192 | * iscsi_create_session - create iscsi class session |
198 | struct list_head connections; | 193 | * @shost: scsi host |
199 | iscsi_sessionh_t sessionh; | 194 | * @transport: iscsi transport |
200 | struct iscsi_transport *transport; | 195 | * |
201 | struct device dev; /* sysfs transport/container device */ | 196 | * This can be called from a LLD or iscsi_transport |
202 | }; | 197 | **/ |
198 | struct iscsi_cls_session * | ||
199 | iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *transport) | ||
200 | { | ||
201 | struct iscsi_cls_session *session; | ||
202 | int err; | ||
203 | |||
204 | if (!try_module_get(transport->owner)) | ||
205 | return NULL; | ||
206 | |||
207 | session = kzalloc(sizeof(*session), GFP_KERNEL); | ||
208 | if (!session) | ||
209 | goto module_put; | ||
210 | session->transport = transport; | ||
211 | |||
212 | /* this is released in the dev's release function */ | ||
213 | scsi_host_get(shost); | ||
214 | snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", shost->host_no); | ||
215 | session->dev.parent = &shost->shost_gendev; | ||
216 | session->dev.release = iscsi_session_release; | ||
217 | err = device_register(&session->dev); | ||
218 | if (err) { | ||
219 | dev_printk(KERN_ERR, &session->dev, "iscsi: could not " | ||
220 | "register session's dev\n"); | ||
221 | goto free_session; | ||
222 | } | ||
223 | transport_register_device(&session->dev); | ||
224 | |||
225 | return session; | ||
226 | |||
227 | free_session: | ||
228 | kfree(session); | ||
229 | module_put: | ||
230 | module_put(transport->owner); | ||
231 | return NULL; | ||
232 | } | ||
233 | |||
234 | EXPORT_SYMBOL_GPL(iscsi_create_session); | ||
235 | |||
236 | /** | ||
237 | * iscsi_destroy_session - destroy iscsi session | ||
238 | * @session: iscsi_session | ||
239 | * | ||
240 | * Can be called by a LLD or iscsi_transport. There must not be | ||
241 | * any running connections. | ||
242 | **/ | ||
243 | int iscsi_destroy_session(struct iscsi_cls_session *session) | ||
244 | { | ||
245 | transport_unregister_device(&session->dev); | ||
246 | device_unregister(&session->dev); | ||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | EXPORT_SYMBOL_GPL(iscsi_destroy_session); | ||
251 | |||
252 | static void iscsi_conn_release(struct device *dev) | ||
253 | { | ||
254 | struct iscsi_cls_conn *conn = iscsi_dev_to_conn(dev); | ||
255 | struct device *parent = conn->dev.parent; | ||
256 | |||
257 | kfree(conn); | ||
258 | put_device(parent); | ||
259 | } | ||
260 | |||
261 | static int iscsi_is_conn_dev(const struct device *dev) | ||
262 | { | ||
263 | return dev->release == iscsi_conn_release; | ||
264 | } | ||
265 | |||
266 | /** | ||
267 | * iscsi_create_conn - create iscsi class connection | ||
268 | * @session: iscsi cls session | ||
269 | * @cid: connection id | ||
270 | * | ||
271 | * This can be called from a LLD or iscsi_transport. The connection | ||
272 | * is child of the session so cid must be unique for all connections | ||
273 | * on the session. | ||
274 | **/ | ||
275 | struct iscsi_cls_conn * | ||
276 | iscsi_create_conn(struct iscsi_cls_session *session, uint32_t cid) | ||
277 | { | ||
278 | struct iscsi_transport *transport = session->transport; | ||
279 | struct Scsi_Host *shost = iscsi_session_to_shost(session); | ||
280 | struct iscsi_cls_conn *conn; | ||
281 | int err; | ||
282 | |||
283 | conn = kzalloc(sizeof(*conn) + transport->conndata_size, GFP_KERNEL); | ||
284 | if (!conn) | ||
285 | return NULL; | ||
286 | |||
287 | if (transport->conndata_size) | ||
288 | conn->dd_data = &conn[1]; | ||
289 | |||
290 | INIT_LIST_HEAD(&conn->conn_list); | ||
291 | conn->transport = transport; | ||
292 | |||
293 | /* this is released in the dev's release function */ | ||
294 | if (!get_device(&session->dev)) | ||
295 | goto free_conn; | ||
296 | snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", | ||
297 | shost->host_no, cid); | ||
298 | conn->dev.parent = &session->dev; | ||
299 | conn->dev.release = iscsi_conn_release; | ||
300 | err = device_register(&conn->dev); | ||
301 | if (err) { | ||
302 | dev_printk(KERN_ERR, &conn->dev, "iscsi: could not register " | ||
303 | "connection's dev\n"); | ||
304 | goto release_parent_ref; | ||
305 | } | ||
306 | transport_register_device(&conn->dev); | ||
307 | return conn; | ||
308 | |||
309 | release_parent_ref: | ||
310 | put_device(&session->dev); | ||
311 | free_conn: | ||
312 | kfree(conn); | ||
313 | return NULL; | ||
314 | } | ||
315 | |||
316 | EXPORT_SYMBOL_GPL(iscsi_create_conn); | ||
317 | |||
318 | /** | ||
319 | * iscsi_destroy_conn - destroy iscsi class connection | ||
320 | * @session: iscsi cls session | ||
321 | * | ||
322 | * This can be called from a LLD or iscsi_transport. | ||
323 | **/ | ||
324 | int iscsi_destroy_conn(struct iscsi_cls_conn *conn) | ||
325 | { | ||
326 | transport_unregister_device(&conn->dev); | ||
327 | device_unregister(&conn->dev); | ||
328 | return 0; | ||
329 | } | ||
330 | |||
331 | EXPORT_SYMBOL_GPL(iscsi_destroy_conn); | ||
332 | |||
333 | /* | ||
334 | * These functions are used only by software iscsi_transports | ||
335 | * which do not allocate and more their scsi_hosts since this | ||
336 | * is initiated from userspace. | ||
337 | */ | ||
338 | |||
339 | /* | ||
340 | * iSCSI Session's hostdata organization: | ||
341 | * | ||
342 | * *------------------* <== hostdata_session(host->hostdata) | ||
343 | * | ptr to class sess| | ||
344 | * |------------------| <== iscsi_hostdata(host->hostdata) | ||
345 | * | transport's data | | ||
346 | * *------------------* | ||
347 | */ | ||
348 | |||
349 | #define hostdata_privsize(_t) (sizeof(unsigned long) + _t->hostdata_size + \ | ||
350 | _t->hostdata_size % sizeof(unsigned long)) | ||
351 | |||
352 | #define hostdata_session(_hostdata) (iscsi_ptr(*(unsigned long *)_hostdata)) | ||
353 | |||
354 | /** | ||
355 | * iscsi_transport_create_session - create iscsi cls session and host | ||
356 | * scsit: scsi transport template | ||
357 | * transport: iscsi transport template | ||
358 | * | ||
359 | * This can be used by software iscsi_transports that allocate | ||
360 | * a session per scsi host. | ||
361 | **/ | ||
362 | struct Scsi_Host * | ||
363 | iscsi_transport_create_session(struct scsi_transport_template *scsit, | ||
364 | struct iscsi_transport *transport) | ||
365 | { | ||
366 | struct iscsi_cls_session *session; | ||
367 | struct Scsi_Host *shost; | ||
368 | |||
369 | shost = scsi_host_alloc(transport->host_template, | ||
370 | hostdata_privsize(transport)); | ||
371 | if (!shost) { | ||
372 | printk(KERN_ERR "iscsi: can not allocate SCSI host for " | ||
373 | "session\n"); | ||
374 | return NULL; | ||
375 | } | ||
376 | |||
377 | shost->max_id = 1; | ||
378 | shost->max_channel = 0; | ||
379 | shost->max_lun = transport->max_lun; | ||
380 | shost->max_cmd_len = transport->max_cmd_len; | ||
381 | shost->transportt = scsit; | ||
382 | shost->transportt->create_work_queue = 1; | ||
383 | |||
384 | if (scsi_add_host(shost, NULL)) | ||
385 | goto free_host; | ||
386 | |||
387 | session = iscsi_create_session(shost, transport); | ||
388 | if (!session) | ||
389 | goto remove_host; | ||
203 | 390 | ||
204 | #define iscsi_dev_to_if_session(_dev) \ | 391 | *(unsigned long*)shost->hostdata = (unsigned long)session; |
205 | container_of(_dev, struct iscsi_if_session, dev) | 392 | return shost; |
393 | |||
394 | remove_host: | ||
395 | scsi_remove_host(shost); | ||
396 | free_host: | ||
397 | scsi_host_put(shost); | ||
398 | return NULL; | ||
399 | } | ||
206 | 400 | ||
207 | #define iscsi_cdev_to_if_session(_cdev) \ | 401 | EXPORT_SYMBOL_GPL(iscsi_transport_create_session); |
208 | iscsi_dev_to_if_session(_cdev->dev) | ||
209 | 402 | ||
210 | #define iscsi_if_session_to_shost(_session) \ | 403 | /** |
211 | dev_to_shost(_session->dev.parent) | 404 | * iscsi_transport_destroy_session - destroy session and scsi host |
405 | * shost: scsi host | ||
406 | * | ||
407 | * This can be used by software iscsi_transports that allocate | ||
408 | * a session per scsi host. | ||
409 | **/ | ||
410 | int iscsi_transport_destroy_session(struct Scsi_Host *shost) | ||
411 | { | ||
412 | struct iscsi_cls_session *session; | ||
212 | 413 | ||
213 | static struct iscsi_if_conn* | 414 | scsi_remove_host(shost); |
415 | session = hostdata_session(shost->hostdata); | ||
416 | iscsi_destroy_session(session); | ||
417 | /* ref from host alloc */ | ||
418 | scsi_host_put(shost); | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | EXPORT_SYMBOL_GPL(iscsi_transport_destroy_session); | ||
423 | |||
424 | /* | ||
425 | * iscsi interface functions | ||
426 | */ | ||
427 | static struct iscsi_cls_conn* | ||
214 | iscsi_if_find_conn(uint64_t key) | 428 | iscsi_if_find_conn(uint64_t key) |
215 | { | 429 | { |
216 | unsigned long flags; | 430 | unsigned long flags; |
217 | struct iscsi_if_conn *conn; | 431 | struct iscsi_cls_conn *conn; |
218 | 432 | ||
219 | spin_lock_irqsave(&connlock, flags); | 433 | spin_lock_irqsave(&connlock, flags); |
220 | list_for_each_entry(conn, &connlist, conn_list) | 434 | list_for_each_entry(conn, &connlist, conn_list) |
@@ -249,7 +463,7 @@ static inline struct list_head *skb_to_lh(struct sk_buff *skb) | |||
249 | } | 463 | } |
250 | 464 | ||
251 | static void* | 465 | static void* |
252 | mempool_zone_alloc_skb(gfp_t gfp_mask, void *pool_data) | 466 | mempool_zone_alloc_skb(unsigned int gfp_mask, void *pool_data) |
253 | { | 467 | { |
254 | struct mempool_zone *zone = pool_data; | 468 | struct mempool_zone *zone = pool_data; |
255 | 469 | ||
@@ -281,14 +495,21 @@ mempool_zone_complete(struct mempool_zone *zone) | |||
281 | spin_unlock_irqrestore(&zone->freelock, flags); | 495 | spin_unlock_irqrestore(&zone->freelock, flags); |
282 | } | 496 | } |
283 | 497 | ||
284 | static int | 498 | static struct mempool_zone * |
285 | mempool_zone_init(struct mempool_zone *zp, unsigned max, unsigned size, | 499 | mempool_zone_init(unsigned max, unsigned size, unsigned hiwat) |
286 | unsigned hiwat) | ||
287 | { | 500 | { |
501 | struct mempool_zone *zp; | ||
502 | |||
503 | zp = kzalloc(sizeof(*zp), GFP_KERNEL); | ||
504 | if (!zp) | ||
505 | return NULL; | ||
506 | |||
288 | zp->pool = mempool_create(max, mempool_zone_alloc_skb, | 507 | zp->pool = mempool_create(max, mempool_zone_alloc_skb, |
289 | mempool_zone_free_skb, zp); | 508 | mempool_zone_free_skb, zp); |
290 | if (!zp->pool) | 509 | if (!zp->pool) { |
291 | return -ENOMEM; | 510 | kfree(zp); |
511 | return NULL; | ||
512 | } | ||
292 | 513 | ||
293 | zp->size = size; | 514 | zp->size = size; |
294 | zp->hiwat = hiwat; | 515 | zp->hiwat = hiwat; |
@@ -297,9 +518,14 @@ mempool_zone_init(struct mempool_zone *zp, unsigned max, unsigned size, | |||
297 | spin_lock_init(&zp->freelock); | 518 | spin_lock_init(&zp->freelock); |
298 | atomic_set(&zp->allocated, 0); | 519 | atomic_set(&zp->allocated, 0); |
299 | 520 | ||
300 | return 0; | 521 | return zp; |
301 | } | 522 | } |
302 | 523 | ||
524 | static void mempool_zone_destroy(struct mempool_zone *zp) | ||
525 | { | ||
526 | mempool_destroy(zp->pool); | ||
527 | kfree(zp); | ||
528 | } | ||
303 | 529 | ||
304 | static struct sk_buff* | 530 | static struct sk_buff* |
305 | mempool_zone_get_skb(struct mempool_zone *zone) | 531 | mempool_zone_get_skb(struct mempool_zone *zone) |
@@ -339,7 +565,7 @@ int iscsi_recv_pdu(iscsi_connh_t connh, struct iscsi_hdr *hdr, | |||
339 | struct nlmsghdr *nlh; | 565 | struct nlmsghdr *nlh; |
340 | struct sk_buff *skb; | 566 | struct sk_buff *skb; |
341 | struct iscsi_uevent *ev; | 567 | struct iscsi_uevent *ev; |
342 | struct iscsi_if_conn *conn; | 568 | struct iscsi_cls_conn *conn; |
343 | char *pdu; | 569 | char *pdu; |
344 | int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) + | 570 | int len = NLMSG_SPACE(sizeof(*ev) + sizeof(struct iscsi_hdr) + |
345 | data_size); | 571 | data_size); |
@@ -347,13 +573,13 @@ int iscsi_recv_pdu(iscsi_connh_t connh, struct iscsi_hdr *hdr, | |||
347 | conn = iscsi_if_find_conn(connh); | 573 | conn = iscsi_if_find_conn(connh); |
348 | BUG_ON(!conn); | 574 | BUG_ON(!conn); |
349 | 575 | ||
350 | mempool_zone_complete(&conn->z_pdu); | 576 | mempool_zone_complete(conn->z_pdu); |
351 | 577 | ||
352 | skb = mempool_zone_get_skb(&conn->z_pdu); | 578 | skb = mempool_zone_get_skb(conn->z_pdu); |
353 | if (!skb) { | 579 | if (!skb) { |
354 | iscsi_conn_error(connh, ISCSI_ERR_CONN_FAILED); | 580 | iscsi_conn_error(connh, ISCSI_ERR_CONN_FAILED); |
355 | printk(KERN_ERR "iscsi%d: can not deliver control PDU: OOM\n", | 581 | dev_printk(KERN_ERR, &conn->dev, "iscsi: can not deliver " |
356 | conn->host->host_no); | 582 | "control PDU: OOM\n"); |
357 | return -ENOMEM; | 583 | return -ENOMEM; |
358 | } | 584 | } |
359 | 585 | ||
@@ -362,14 +588,14 @@ int iscsi_recv_pdu(iscsi_connh_t connh, struct iscsi_hdr *hdr, | |||
362 | memset(ev, 0, sizeof(*ev)); | 588 | memset(ev, 0, sizeof(*ev)); |
363 | ev->transport_handle = iscsi_handle(conn->transport); | 589 | ev->transport_handle = iscsi_handle(conn->transport); |
364 | ev->type = ISCSI_KEVENT_RECV_PDU; | 590 | ev->type = ISCSI_KEVENT_RECV_PDU; |
365 | if (atomic_read(&conn->z_pdu.allocated) >= conn->z_pdu.hiwat) | 591 | if (atomic_read(&conn->z_pdu->allocated) >= conn->z_pdu->hiwat) |
366 | ev->iferror = -ENOMEM; | 592 | ev->iferror = -ENOMEM; |
367 | ev->r.recv_req.conn_handle = connh; | 593 | ev->r.recv_req.conn_handle = connh; |
368 | pdu = (char*)ev + sizeof(*ev); | 594 | pdu = (char*)ev + sizeof(*ev); |
369 | memcpy(pdu, hdr, sizeof(struct iscsi_hdr)); | 595 | memcpy(pdu, hdr, sizeof(struct iscsi_hdr)); |
370 | memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size); | 596 | memcpy(pdu + sizeof(struct iscsi_hdr), data, data_size); |
371 | 597 | ||
372 | return iscsi_unicast_skb(&conn->z_pdu, skb); | 598 | return iscsi_unicast_skb(conn->z_pdu, skb); |
373 | } | 599 | } |
374 | EXPORT_SYMBOL_GPL(iscsi_recv_pdu); | 600 | EXPORT_SYMBOL_GPL(iscsi_recv_pdu); |
375 | 601 | ||
@@ -378,18 +604,18 @@ void iscsi_conn_error(iscsi_connh_t connh, enum iscsi_err error) | |||
378 | struct nlmsghdr *nlh; | 604 | struct nlmsghdr *nlh; |
379 | struct sk_buff *skb; | 605 | struct sk_buff *skb; |
380 | struct iscsi_uevent *ev; | 606 | struct iscsi_uevent *ev; |
381 | struct iscsi_if_conn *conn; | 607 | struct iscsi_cls_conn *conn; |
382 | int len = NLMSG_SPACE(sizeof(*ev)); | 608 | int len = NLMSG_SPACE(sizeof(*ev)); |
383 | 609 | ||
384 | conn = iscsi_if_find_conn(connh); | 610 | conn = iscsi_if_find_conn(connh); |
385 | BUG_ON(!conn); | 611 | BUG_ON(!conn); |
386 | 612 | ||
387 | mempool_zone_complete(&conn->z_error); | 613 | mempool_zone_complete(conn->z_error); |
388 | 614 | ||
389 | skb = mempool_zone_get_skb(&conn->z_error); | 615 | skb = mempool_zone_get_skb(conn->z_error); |
390 | if (!skb) { | 616 | if (!skb) { |
391 | printk(KERN_ERR "iscsi%d: gracefully ignored conn error (%d)\n", | 617 | dev_printk(KERN_ERR, &conn->dev, "iscsi: gracefully ignored " |
392 | conn->host->host_no, error); | 618 | "conn error (%d)\n", error); |
393 | return; | 619 | return; |
394 | } | 620 | } |
395 | 621 | ||
@@ -397,15 +623,15 @@ void iscsi_conn_error(iscsi_connh_t connh, enum iscsi_err error) | |||
397 | ev = NLMSG_DATA(nlh); | 623 | ev = NLMSG_DATA(nlh); |
398 | ev->transport_handle = iscsi_handle(conn->transport); | 624 | ev->transport_handle = iscsi_handle(conn->transport); |
399 | ev->type = ISCSI_KEVENT_CONN_ERROR; | 625 | ev->type = ISCSI_KEVENT_CONN_ERROR; |
400 | if (atomic_read(&conn->z_error.allocated) >= conn->z_error.hiwat) | 626 | if (atomic_read(&conn->z_error->allocated) >= conn->z_error->hiwat) |
401 | ev->iferror = -ENOMEM; | 627 | ev->iferror = -ENOMEM; |
402 | ev->r.connerror.error = error; | 628 | ev->r.connerror.error = error; |
403 | ev->r.connerror.conn_handle = connh; | 629 | ev->r.connerror.conn_handle = connh; |
404 | 630 | ||
405 | iscsi_unicast_skb(&conn->z_error, skb); | 631 | iscsi_unicast_skb(conn->z_error, skb); |
406 | 632 | ||
407 | printk(KERN_INFO "iscsi%d: detected conn error (%d)\n", | 633 | dev_printk(KERN_INFO, &conn->dev, "iscsi: detected conn error (%d)\n", |
408 | conn->host->host_no, error); | 634 | error); |
409 | } | 635 | } |
410 | EXPORT_SYMBOL_GPL(iscsi_conn_error); | 636 | EXPORT_SYMBOL_GPL(iscsi_conn_error); |
411 | 637 | ||
@@ -419,9 +645,9 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, | |||
419 | int flags = multi ? NLM_F_MULTI : 0; | 645 | int flags = multi ? NLM_F_MULTI : 0; |
420 | int t = done ? NLMSG_DONE : type; | 646 | int t = done ? NLMSG_DONE : type; |
421 | 647 | ||
422 | mempool_zone_complete(&z_reply); | 648 | mempool_zone_complete(z_reply); |
423 | 649 | ||
424 | skb = mempool_zone_get_skb(&z_reply); | 650 | skb = mempool_zone_get_skb(z_reply); |
425 | /* | 651 | /* |
426 | * FIXME: | 652 | * FIXME: |
427 | * user is supposed to react on iferror == -ENOMEM; | 653 | * user is supposed to react on iferror == -ENOMEM; |
@@ -432,366 +658,197 @@ iscsi_if_send_reply(int pid, int seq, int type, int done, int multi, | |||
432 | nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0); | 658 | nlh = __nlmsg_put(skb, pid, seq, t, (len - sizeof(*nlh)), 0); |
433 | nlh->nlmsg_flags = flags; | 659 | nlh->nlmsg_flags = flags; |
434 | memcpy(NLMSG_DATA(nlh), payload, size); | 660 | memcpy(NLMSG_DATA(nlh), payload, size); |
435 | return iscsi_unicast_skb(&z_reply, skb); | 661 | return iscsi_unicast_skb(z_reply, skb); |
436 | } | 662 | } |
437 | 663 | ||
438 | /* | 664 | static int |
439 | * iSCSI Session's hostdata organization: | 665 | iscsi_if_get_stats(struct iscsi_transport *transport, struct sk_buff *skb, |
440 | * | 666 | struct nlmsghdr *nlh) |
441 | * *------------------* <== host->hostdata | 667 | { |
442 | * | transport | | 668 | struct iscsi_uevent *ev = NLMSG_DATA(nlh); |
443 | * |------------------| <== iscsi_hostdata(host->hostdata) | 669 | struct iscsi_stats *stats; |
444 | * | transport's data | | 670 | struct sk_buff *skbstat; |
445 | * |------------------| <== hostdata_session(host->hostdata) | 671 | struct iscsi_cls_conn *conn; |
446 | * | interface's data | | 672 | struct nlmsghdr *nlhstat; |
447 | * *------------------* | 673 | struct iscsi_uevent *evstat; |
448 | */ | 674 | int len = NLMSG_SPACE(sizeof(*ev) + |
675 | sizeof(struct iscsi_stats) + | ||
676 | sizeof(struct iscsi_stats_custom) * | ||
677 | ISCSI_STATS_CUSTOM_MAX); | ||
678 | int err = 0; | ||
449 | 679 | ||
450 | #define hostdata_privsize(_t) (sizeof(unsigned long) + _t->hostdata_size + \ | 680 | conn = iscsi_if_find_conn(ev->u.get_stats.conn_handle); |
451 | _t->hostdata_size % sizeof(unsigned long) + \ | 681 | if (!conn) |
452 | sizeof(struct iscsi_if_session)) | 682 | return -EEXIST; |
453 | 683 | ||
454 | #define hostdata_session(_hostdata) ((void*)_hostdata + sizeof(unsigned long) + \ | 684 | do { |
455 | ((struct iscsi_transport *) \ | 685 | int actual_size; |
456 | iscsi_ptr(*(uint64_t *)_hostdata))->hostdata_size) | ||
457 | 686 | ||
458 | static void iscsi_if_session_dev_release(struct device *dev) | 687 | mempool_zone_complete(conn->z_pdu); |
459 | { | ||
460 | struct iscsi_if_session *session = iscsi_dev_to_if_session(dev); | ||
461 | struct iscsi_transport *transport = session->transport; | ||
462 | struct Scsi_Host *shost = iscsi_if_session_to_shost(session); | ||
463 | struct iscsi_if_conn *conn, *tmp; | ||
464 | unsigned long flags; | ||
465 | 688 | ||
466 | /* now free connections */ | 689 | skbstat = mempool_zone_get_skb(conn->z_pdu); |
467 | spin_lock_irqsave(&connlock, flags); | 690 | if (!skbstat) { |
468 | list_for_each_entry_safe(conn, tmp, &session->connections, | 691 | dev_printk(KERN_ERR, &conn->dev, "iscsi: can not " |
469 | session_list) { | 692 | "deliver stats: OOM\n"); |
470 | list_del(&conn->session_list); | 693 | return -ENOMEM; |
471 | mempool_destroy(conn->z_pdu.pool); | 694 | } |
472 | mempool_destroy(conn->z_error.pool); | 695 | |
473 | kfree(conn); | 696 | nlhstat = __nlmsg_put(skbstat, daemon_pid, 0, 0, |
474 | } | 697 | (len - sizeof(*nlhstat)), 0); |
475 | spin_unlock_irqrestore(&connlock, flags); | 698 | evstat = NLMSG_DATA(nlhstat); |
476 | scsi_host_put(shost); | 699 | memset(evstat, 0, sizeof(*evstat)); |
477 | module_put(transport->owner); | 700 | evstat->transport_handle = iscsi_handle(conn->transport); |
701 | evstat->type = nlh->nlmsg_type; | ||
702 | if (atomic_read(&conn->z_pdu->allocated) >= conn->z_pdu->hiwat) | ||
703 | evstat->iferror = -ENOMEM; | ||
704 | evstat->u.get_stats.conn_handle = | ||
705 | ev->u.get_stats.conn_handle; | ||
706 | stats = (struct iscsi_stats *) | ||
707 | ((char*)evstat + sizeof(*evstat)); | ||
708 | memset(stats, 0, sizeof(*stats)); | ||
709 | |||
710 | transport->get_stats(ev->u.get_stats.conn_handle, stats); | ||
711 | actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) + | ||
712 | sizeof(struct iscsi_stats) + | ||
713 | sizeof(struct iscsi_stats_custom) * | ||
714 | stats->custom_length); | ||
715 | actual_size -= sizeof(*nlhstat); | ||
716 | actual_size = NLMSG_LENGTH(actual_size); | ||
717 | skb_trim(skb, NLMSG_ALIGN(actual_size)); | ||
718 | nlhstat->nlmsg_len = actual_size; | ||
719 | |||
720 | err = iscsi_unicast_skb(conn->z_pdu, skbstat); | ||
721 | } while (err < 0 && err != -ECONNREFUSED); | ||
722 | |||
723 | return err; | ||
478 | } | 724 | } |
479 | 725 | ||
480 | static int | 726 | static int |
481 | iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) | 727 | iscsi_if_create_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) |
482 | { | 728 | { |
483 | struct iscsi_transport *transport = priv->iscsi_transport; | 729 | struct iscsi_transport *transport = priv->iscsi_transport; |
484 | struct iscsi_if_session *session; | ||
485 | struct Scsi_Host *shost; | 730 | struct Scsi_Host *shost; |
486 | unsigned long flags; | ||
487 | int error; | ||
488 | |||
489 | if (!try_module_get(transport->owner)) | ||
490 | return -EPERM; | ||
491 | 731 | ||
492 | shost = scsi_host_alloc(transport->host_template, | 732 | if (!transport->create_session) |
493 | hostdata_privsize(transport)); | 733 | return -EINVAL; |
494 | if (!shost) { | ||
495 | ev->r.c_session_ret.session_handle = iscsi_handle(NULL); | ||
496 | printk(KERN_ERR "iscsi: can not allocate SCSI host for " | ||
497 | "session\n"); | ||
498 | error = -ENOMEM; | ||
499 | goto out_module_put; | ||
500 | } | ||
501 | shost->max_id = 1; | ||
502 | shost->max_channel = 0; | ||
503 | shost->max_lun = transport->max_lun; | ||
504 | shost->max_cmd_len = transport->max_cmd_len; | ||
505 | shost->transportt = &priv->t; | ||
506 | |||
507 | /* store struct iscsi_transport in hostdata */ | ||
508 | *(uint64_t*)shost->hostdata = ev->transport_handle; | ||
509 | 734 | ||
510 | ev->r.c_session_ret.session_handle = transport->create_session( | 735 | shost = transport->create_session(&priv->t, |
511 | ev->u.c_session.initial_cmdsn, shost); | 736 | ev->u.c_session.initial_cmdsn); |
512 | if (ev->r.c_session_ret.session_handle == iscsi_handle(NULL)) { | 737 | if (!shost) |
513 | error = 0; | 738 | return -ENOMEM; |
514 | goto out_host_put; | ||
515 | } | ||
516 | 739 | ||
517 | /* host_no becomes assigned SID */ | 740 | ev->r.c_session_ret.session_handle = iscsi_handle(iscsi_hostdata(shost->hostdata)); |
518 | ev->r.c_session_ret.sid = shost->host_no; | 741 | ev->r.c_session_ret.sid = shost->host_no; |
519 | /* initialize session */ | ||
520 | session = hostdata_session(shost->hostdata); | ||
521 | INIT_LIST_HEAD(&session->connections); | ||
522 | INIT_LIST_HEAD(&session->list); | ||
523 | session->sessionh = ev->r.c_session_ret.session_handle; | ||
524 | session->transport = transport; | ||
525 | |||
526 | error = scsi_add_host(shost, NULL); | ||
527 | if (error) | ||
528 | goto out_destroy_session; | ||
529 | |||
530 | /* | ||
531 | * this is released in the dev's release function) | ||
532 | */ | ||
533 | scsi_host_get(shost); | ||
534 | snprintf(session->dev.bus_id, BUS_ID_SIZE, "session%u", shost->host_no); | ||
535 | session->dev.parent = &shost->shost_gendev; | ||
536 | session->dev.release = iscsi_if_session_dev_release; | ||
537 | error = device_register(&session->dev); | ||
538 | if (error) { | ||
539 | printk(KERN_ERR "iscsi: could not register session%d's dev\n", | ||
540 | shost->host_no); | ||
541 | goto out_remove_host; | ||
542 | } | ||
543 | transport_register_device(&session->dev); | ||
544 | |||
545 | /* add this session to the list of active sessions */ | ||
546 | spin_lock_irqsave(&priv->session_lock, flags); | ||
547 | list_add(&session->list, &priv->sessions); | ||
548 | spin_unlock_irqrestore(&priv->session_lock, flags); | ||
549 | |||
550 | return 0; | 742 | return 0; |
551 | |||
552 | out_remove_host: | ||
553 | scsi_remove_host(shost); | ||
554 | out_destroy_session: | ||
555 | transport->destroy_session(ev->r.c_session_ret.session_handle); | ||
556 | ev->r.c_session_ret.session_handle = iscsi_handle(NULL); | ||
557 | out_host_put: | ||
558 | scsi_host_put(shost); | ||
559 | out_module_put: | ||
560 | module_put(transport->owner); | ||
561 | return error; | ||
562 | } | 743 | } |
563 | 744 | ||
564 | static int | 745 | static int |
565 | iscsi_if_destroy_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) | 746 | iscsi_if_destroy_session(struct iscsi_internal *priv, struct iscsi_uevent *ev) |
566 | { | 747 | { |
567 | struct iscsi_transport *transport = priv->iscsi_transport; | 748 | struct iscsi_transport *transport = priv->iscsi_transport; |
749 | |||
568 | struct Scsi_Host *shost; | 750 | struct Scsi_Host *shost; |
569 | struct iscsi_if_session *session; | 751 | |
570 | unsigned long flags; | 752 | if (!transport->destroy_session) |
571 | struct iscsi_if_conn *conn; | 753 | return -EINVAL; |
572 | int error = 0; | ||
573 | 754 | ||
574 | shost = scsi_host_lookup(ev->u.d_session.sid); | 755 | shost = scsi_host_lookup(ev->u.d_session.sid); |
575 | if (shost == ERR_PTR(-ENXIO)) | 756 | if (shost == ERR_PTR(-ENXIO)) |
576 | return -EEXIST; | 757 | return -EEXIST; |
577 | session = hostdata_session(shost->hostdata); | ||
578 | 758 | ||
579 | /* check if we have active connections */ | 759 | if (transport->destroy_session) |
580 | spin_lock_irqsave(&connlock, flags); | 760 | transport->destroy_session(shost); |
581 | list_for_each_entry(conn, &session->connections, session_list) { | 761 | /* ref from host lookup */ |
582 | if (conn->active) { | 762 | scsi_host_put(shost); |
583 | printk(KERN_ERR "iscsi%d: can not destroy session: " | 763 | return 0; |
584 | "has active connection (%p)\n", | ||
585 | shost->host_no, iscsi_ptr(conn->connh)); | ||
586 | spin_unlock_irqrestore(&connlock, flags); | ||
587 | error = EIO; | ||
588 | goto out_release_ref; | ||
589 | } | ||
590 | } | ||
591 | spin_unlock_irqrestore(&connlock, flags); | ||
592 | |||
593 | scsi_remove_host(shost); | ||
594 | transport->destroy_session(ev->u.d_session.session_handle); | ||
595 | transport_unregister_device(&session->dev); | ||
596 | device_unregister(&session->dev); | ||
597 | |||
598 | /* remove this session from the list of active sessions */ | ||
599 | spin_lock_irqsave(&priv->session_lock, flags); | ||
600 | list_del(&session->list); | ||
601 | spin_unlock_irqrestore(&priv->session_lock, flags); | ||
602 | |||
603 | /* ref from host alloc */ | ||
604 | scsi_host_put(shost); | ||
605 | out_release_ref: | ||
606 | /* ref from host lookup */ | ||
607 | scsi_host_put(shost); | ||
608 | return error; | ||
609 | } | ||
610 | |||
611 | static void iscsi_if_conn_dev_release(struct device *dev) | ||
612 | { | ||
613 | struct iscsi_if_conn *conn = iscsi_dev_to_if_conn(dev); | ||
614 | struct Scsi_Host *shost = conn->host; | ||
615 | |||
616 | scsi_host_put(shost); | ||
617 | } | 764 | } |
618 | 765 | ||
619 | static int | 766 | static int |
620 | iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) | 767 | iscsi_if_create_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev){ |
621 | { | ||
622 | struct iscsi_if_session *session; | ||
623 | struct Scsi_Host *shost; | 768 | struct Scsi_Host *shost; |
624 | struct iscsi_if_conn *conn; | 769 | struct iscsi_cls_conn *conn; |
625 | unsigned long flags; | 770 | unsigned long flags; |
626 | int error; | 771 | |
772 | if (!transport->create_conn) | ||
773 | return -EINVAL; | ||
627 | 774 | ||
628 | shost = scsi_host_lookup(ev->u.c_conn.sid); | 775 | shost = scsi_host_lookup(ev->u.c_conn.sid); |
629 | if (shost == ERR_PTR(-ENXIO)) | 776 | if (shost == ERR_PTR(-ENXIO)) |
630 | return -EEXIST; | 777 | return -EEXIST; |
631 | session = hostdata_session(shost->hostdata); | ||
632 | 778 | ||
633 | conn = kmalloc(sizeof(struct iscsi_if_conn), GFP_KERNEL); | 779 | conn = transport->create_conn(shost, ev->u.c_conn.cid); |
634 | if (!conn) { | 780 | if (!conn) |
635 | error = -ENOMEM; | 781 | goto release_ref; |
636 | goto out_release_ref; | ||
637 | } | ||
638 | memset(conn, 0, sizeof(struct iscsi_if_conn)); | ||
639 | INIT_LIST_HEAD(&conn->session_list); | ||
640 | INIT_LIST_HEAD(&conn->conn_list); | ||
641 | conn->host = shost; | ||
642 | conn->transport = transport; | ||
643 | 782 | ||
644 | error = mempool_zone_init(&conn->z_pdu, Z_MAX_PDU, | 783 | conn->z_pdu = mempool_zone_init(Z_MAX_PDU, |
645 | NLMSG_SPACE(sizeof(struct iscsi_uevent) + | 784 | NLMSG_SPACE(sizeof(struct iscsi_uevent) + |
646 | sizeof(struct iscsi_hdr) + | 785 | sizeof(struct iscsi_hdr) + |
647 | DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH), | 786 | DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH), |
648 | Z_HIWAT_PDU); | 787 | Z_HIWAT_PDU); |
649 | if (error) { | 788 | if (!conn->z_pdu) { |
650 | printk(KERN_ERR "iscsi%d: can not allocate pdu zone for new " | 789 | dev_printk(KERN_ERR, &conn->dev, "iscsi: can not allocate " |
651 | "conn\n", shost->host_no); | 790 | "pdu zone for new conn\n"); |
652 | goto out_free_conn; | 791 | goto destroy_conn; |
653 | } | 792 | } |
654 | error = mempool_zone_init(&conn->z_error, Z_MAX_ERROR, | 793 | |
794 | conn->z_error = mempool_zone_init(Z_MAX_ERROR, | ||
655 | NLMSG_SPACE(sizeof(struct iscsi_uevent)), | 795 | NLMSG_SPACE(sizeof(struct iscsi_uevent)), |
656 | Z_HIWAT_ERROR); | 796 | Z_HIWAT_ERROR); |
657 | if (error) { | 797 | if (!conn->z_error) { |
658 | printk(KERN_ERR "iscsi%d: can not allocate error zone for " | 798 | dev_printk(KERN_ERR, &conn->dev, "iscsi: can not allocate " |
659 | "new conn\n", shost->host_no); | 799 | "error zone for new conn\n"); |
660 | goto out_free_pdu_pool; | 800 | goto free_pdu_pool; |
661 | } | ||
662 | |||
663 | ev->r.handle = transport->create_conn(ev->u.c_conn.session_handle, | ||
664 | ev->u.c_conn.cid); | ||
665 | if (!ev->r.handle) { | ||
666 | error = -ENODEV; | ||
667 | goto out_free_error_pool; | ||
668 | } | 801 | } |
669 | 802 | ||
670 | conn->connh = ev->r.handle; | 803 | ev->r.handle = conn->connh = iscsi_handle(conn->dd_data); |
671 | |||
672 | /* | ||
673 | * this is released in the dev's release function | ||
674 | */ | ||
675 | if (!scsi_host_get(shost)) | ||
676 | goto out_destroy_conn; | ||
677 | snprintf(conn->dev.bus_id, BUS_ID_SIZE, "connection%d:%u", | ||
678 | shost->host_no, ev->u.c_conn.cid); | ||
679 | conn->dev.parent = &session->dev; | ||
680 | conn->dev.release = iscsi_if_conn_dev_release; | ||
681 | error = device_register(&conn->dev); | ||
682 | if (error) { | ||
683 | printk(KERN_ERR "iscsi%d: could not register connections%u " | ||
684 | "dev\n", shost->host_no, ev->u.c_conn.cid); | ||
685 | goto out_release_parent_ref; | ||
686 | } | ||
687 | transport_register_device(&conn->dev); | ||
688 | 804 | ||
689 | spin_lock_irqsave(&connlock, flags); | 805 | spin_lock_irqsave(&connlock, flags); |
690 | list_add(&conn->conn_list, &connlist); | 806 | list_add(&conn->conn_list, &connlist); |
691 | list_add(&conn->session_list, &session->connections); | ||
692 | conn->active = 1; | 807 | conn->active = 1; |
693 | spin_unlock_irqrestore(&connlock, flags); | 808 | spin_unlock_irqrestore(&connlock, flags); |
694 | 809 | ||
695 | scsi_host_put(shost); | 810 | scsi_host_put(shost); |
696 | return 0; | 811 | return 0; |
697 | 812 | ||
698 | out_release_parent_ref: | 813 | free_pdu_pool: |
814 | mempool_zone_destroy(conn->z_pdu); | ||
815 | destroy_conn: | ||
816 | if (transport->destroy_conn) | ||
817 | transport->destroy_conn(conn->dd_data); | ||
818 | release_ref: | ||
699 | scsi_host_put(shost); | 819 | scsi_host_put(shost); |
700 | out_destroy_conn: | 820 | return -ENOMEM; |
701 | transport->destroy_conn(ev->r.handle); | ||
702 | out_free_error_pool: | ||
703 | mempool_destroy(conn->z_error.pool); | ||
704 | out_free_pdu_pool: | ||
705 | mempool_destroy(conn->z_pdu.pool); | ||
706 | out_free_conn: | ||
707 | kfree(conn); | ||
708 | out_release_ref: | ||
709 | scsi_host_put(shost); | ||
710 | return error; | ||
711 | } | 821 | } |
712 | 822 | ||
713 | static int | 823 | static int |
714 | iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) | 824 | iscsi_if_destroy_conn(struct iscsi_transport *transport, struct iscsi_uevent *ev) |
715 | { | 825 | { |
716 | unsigned long flags; | 826 | unsigned long flags; |
717 | struct iscsi_if_conn *conn; | 827 | struct iscsi_cls_conn *conn; |
828 | struct mempool_zone *z_error, *z_pdu; | ||
718 | 829 | ||
719 | conn = iscsi_if_find_conn(ev->u.d_conn.conn_handle); | 830 | conn = iscsi_if_find_conn(ev->u.d_conn.conn_handle); |
720 | if (!conn) | 831 | if (!conn) |
721 | return -EEXIST; | 832 | return -EEXIST; |
722 | 833 | ||
723 | transport->destroy_conn(ev->u.d_conn.conn_handle); | 834 | if (!transport->destroy_conn) |
835 | return -EINVAL; | ||
724 | 836 | ||
725 | spin_lock_irqsave(&connlock, flags); | 837 | spin_lock_irqsave(&connlock, flags); |
726 | conn->active = 0; | 838 | conn->active = 0; |
727 | list_del(&conn->conn_list); | 839 | list_del(&conn->conn_list); |
728 | spin_unlock_irqrestore(&connlock, flags); | 840 | spin_unlock_irqrestore(&connlock, flags); |
729 | 841 | ||
730 | transport_unregister_device(&conn->dev); | 842 | z_pdu = conn->z_pdu; |
731 | device_unregister(&conn->dev); | 843 | z_error = conn->z_error; |
732 | return 0; | ||
733 | } | ||
734 | |||
735 | static int | ||
736 | iscsi_if_get_stats(struct iscsi_transport *transport, struct sk_buff *skb, | ||
737 | struct nlmsghdr *nlh) | ||
738 | { | ||
739 | struct iscsi_uevent *ev = NLMSG_DATA(nlh); | ||
740 | struct iscsi_stats *stats; | ||
741 | struct sk_buff *skbstat; | ||
742 | struct iscsi_if_conn *conn; | ||
743 | struct nlmsghdr *nlhstat; | ||
744 | struct iscsi_uevent *evstat; | ||
745 | int len = NLMSG_SPACE(sizeof(*ev) + | ||
746 | sizeof(struct iscsi_stats) + | ||
747 | sizeof(struct iscsi_stats_custom) * | ||
748 | ISCSI_STATS_CUSTOM_MAX); | ||
749 | int err = 0; | ||
750 | |||
751 | conn = iscsi_if_find_conn(ev->u.get_stats.conn_handle); | ||
752 | if (!conn) | ||
753 | return -EEXIST; | ||
754 | |||
755 | do { | ||
756 | int actual_size; | ||
757 | |||
758 | mempool_zone_complete(&conn->z_pdu); | ||
759 | |||
760 | skbstat = mempool_zone_get_skb(&conn->z_pdu); | ||
761 | if (!skbstat) { | ||
762 | printk(KERN_ERR "iscsi%d: can not deliver stats: OOM\n", | ||
763 | conn->host->host_no); | ||
764 | return -ENOMEM; | ||
765 | } | ||
766 | |||
767 | nlhstat = __nlmsg_put(skbstat, daemon_pid, 0, 0, | ||
768 | (len - sizeof(*nlhstat)), 0); | ||
769 | evstat = NLMSG_DATA(nlhstat); | ||
770 | memset(evstat, 0, sizeof(*evstat)); | ||
771 | evstat->transport_handle = iscsi_handle(conn->transport); | ||
772 | evstat->type = nlh->nlmsg_type; | ||
773 | if (atomic_read(&conn->z_pdu.allocated) >= conn->z_pdu.hiwat) | ||
774 | evstat->iferror = -ENOMEM; | ||
775 | evstat->u.get_stats.conn_handle = | ||
776 | ev->u.get_stats.conn_handle; | ||
777 | stats = (struct iscsi_stats *) | ||
778 | ((char*)evstat + sizeof(*evstat)); | ||
779 | memset(stats, 0, sizeof(*stats)); | ||
780 | 844 | ||
781 | transport->get_stats(ev->u.get_stats.conn_handle, stats); | 845 | if (transport->destroy_conn) |
782 | actual_size = NLMSG_SPACE(sizeof(struct iscsi_uevent) + | 846 | transport->destroy_conn(conn); |
783 | sizeof(struct iscsi_stats) + | ||
784 | sizeof(struct iscsi_stats_custom) * | ||
785 | stats->custom_length); | ||
786 | actual_size -= sizeof(*nlhstat); | ||
787 | actual_size = NLMSG_LENGTH(actual_size); | ||
788 | skb_trim(skb, NLMSG_ALIGN(actual_size)); | ||
789 | nlhstat->nlmsg_len = actual_size; | ||
790 | 847 | ||
791 | err = iscsi_unicast_skb(&conn->z_pdu, skbstat); | 848 | mempool_zone_destroy(z_pdu); |
792 | } while (err < 0 && err != -ECONNREFUSED); | 849 | mempool_zone_destroy(z_error); |
793 | 850 | ||
794 | return err; | 851 | return 0; |
795 | } | 852 | } |
796 | 853 | ||
797 | static int | 854 | static int |
@@ -881,7 +938,7 @@ iscsi_if_rx(struct sock *sk, int len) | |||
881 | { | 938 | { |
882 | struct sk_buff *skb; | 939 | struct sk_buff *skb; |
883 | 940 | ||
884 | down(&rx_queue_sema); | 941 | mutex_lock(&rx_queue_mutex); |
885 | while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { | 942 | while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) { |
886 | while (skb->len >= NLMSG_SPACE(0)) { | 943 | while (skb->len >= NLMSG_SPACE(0)) { |
887 | int err; | 944 | int err; |
@@ -915,17 +972,20 @@ iscsi_if_rx(struct sock *sk, int len) | |||
915 | err = iscsi_if_send_reply( | 972 | err = iscsi_if_send_reply( |
916 | NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, | 973 | NETLINK_CREDS(skb)->pid, nlh->nlmsg_seq, |
917 | nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); | 974 | nlh->nlmsg_type, 0, 0, ev, sizeof(*ev)); |
918 | if (atomic_read(&z_reply.allocated) >= | 975 | if (atomic_read(&z_reply->allocated) >= |
919 | z_reply.hiwat) | 976 | z_reply->hiwat) |
920 | ev->iferror = -ENOMEM; | 977 | ev->iferror = -ENOMEM; |
921 | } while (err < 0 && err != -ECONNREFUSED); | 978 | } while (err < 0 && err != -ECONNREFUSED); |
922 | skb_pull(skb, rlen); | 979 | skb_pull(skb, rlen); |
923 | } | 980 | } |
924 | kfree_skb(skb); | 981 | kfree_skb(skb); |
925 | } | 982 | } |
926 | up(&rx_queue_sema); | 983 | mutex_unlock(&rx_queue_mutex); |
927 | } | 984 | } |
928 | 985 | ||
986 | #define iscsi_cdev_to_conn(_cdev) \ | ||
987 | iscsi_dev_to_conn(_cdev->dev) | ||
988 | |||
929 | /* | 989 | /* |
930 | * iSCSI connection attrs | 990 | * iSCSI connection attrs |
931 | */ | 991 | */ |
@@ -934,12 +994,10 @@ static ssize_t \ | |||
934 | show_conn_int_param_##param(struct class_device *cdev, char *buf) \ | 994 | show_conn_int_param_##param(struct class_device *cdev, char *buf) \ |
935 | { \ | 995 | { \ |
936 | uint32_t value = 0; \ | 996 | uint32_t value = 0; \ |
937 | struct iscsi_if_conn *conn = iscsi_cdev_to_if_conn(cdev); \ | 997 | struct iscsi_cls_conn *conn = iscsi_cdev_to_conn(cdev); \ |
938 | struct iscsi_internal *priv; \ | 998 | struct iscsi_transport *t = conn->transport; \ |
939 | \ | 999 | \ |
940 | priv = to_iscsi_internal(conn->host->transportt); \ | 1000 | t->get_conn_param(conn->dd_data, param, &value); \ |
941 | if (priv->param_mask & (1 << param)) \ | ||
942 | priv->iscsi_transport->get_param(conn->connh, param, &value); \ | ||
943 | return snprintf(buf, 20, format"\n", value); \ | 1001 | return snprintf(buf, 20, format"\n", value); \ |
944 | } | 1002 | } |
945 | 1003 | ||
@@ -954,6 +1012,9 @@ iscsi_conn_int_attr(data_digest, ISCSI_PARAM_DATADGST_EN, "%d"); | |||
954 | iscsi_conn_int_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN, "%d"); | 1012 | iscsi_conn_int_attr(ifmarker, ISCSI_PARAM_IFMARKER_EN, "%d"); |
955 | iscsi_conn_int_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN, "%d"); | 1013 | iscsi_conn_int_attr(ofmarker, ISCSI_PARAM_OFMARKER_EN, "%d"); |
956 | 1014 | ||
1015 | #define iscsi_cdev_to_session(_cdev) \ | ||
1016 | iscsi_dev_to_session(_cdev->dev) | ||
1017 | |||
957 | /* | 1018 | /* |
958 | * iSCSI session attrs | 1019 | * iSCSI session attrs |
959 | */ | 1020 | */ |
@@ -962,20 +1023,11 @@ static ssize_t \ | |||
962 | show_session_int_param_##param(struct class_device *cdev, char *buf) \ | 1023 | show_session_int_param_##param(struct class_device *cdev, char *buf) \ |
963 | { \ | 1024 | { \ |
964 | uint32_t value = 0; \ | 1025 | uint32_t value = 0; \ |
965 | struct iscsi_if_session *session = iscsi_cdev_to_if_session(cdev); \ | 1026 | struct iscsi_cls_session *session = iscsi_cdev_to_session(cdev); \ |
966 | struct Scsi_Host *shost = iscsi_if_session_to_shost(session); \ | 1027 | struct Scsi_Host *shost = iscsi_session_to_shost(session); \ |
967 | struct iscsi_internal *priv = to_iscsi_internal(shost->transportt); \ | 1028 | struct iscsi_transport *t = session->transport; \ |
968 | struct iscsi_if_conn *conn = NULL; \ | ||
969 | unsigned long flags; \ | ||
970 | \ | ||
971 | spin_lock_irqsave(&connlock, flags); \ | ||
972 | if (!list_empty(&session->connections)) \ | ||
973 | conn = list_entry(session->connections.next, \ | ||
974 | struct iscsi_if_conn, session_list); \ | ||
975 | spin_unlock_irqrestore(&connlock, flags); \ | ||
976 | \ | 1029 | \ |
977 | if (conn && (priv->param_mask & (1 << param))) \ | 1030 | t->get_session_param(shost, param, &value); \ |
978 | priv->iscsi_transport->get_param(conn->connh, param, &value);\ | ||
979 | return snprintf(buf, 20, format"\n", value); \ | 1031 | return snprintf(buf, 20, format"\n", value); \ |
980 | } | 1032 | } |
981 | 1033 | ||
@@ -1004,23 +1056,18 @@ iscsi_session_int_attr(erl, ISCSI_PARAM_ERL, "%d"); | |||
1004 | count++; \ | 1056 | count++; \ |
1005 | } | 1057 | } |
1006 | 1058 | ||
1007 | static int iscsi_is_session_dev(const struct device *dev) | ||
1008 | { | ||
1009 | return dev->release == iscsi_if_session_dev_release; | ||
1010 | } | ||
1011 | |||
1012 | static int iscsi_session_match(struct attribute_container *cont, | 1059 | static int iscsi_session_match(struct attribute_container *cont, |
1013 | struct device *dev) | 1060 | struct device *dev) |
1014 | { | 1061 | { |
1015 | struct iscsi_if_session *session; | 1062 | struct iscsi_cls_session *session; |
1016 | struct Scsi_Host *shost; | 1063 | struct Scsi_Host *shost; |
1017 | struct iscsi_internal *priv; | 1064 | struct iscsi_internal *priv; |
1018 | 1065 | ||
1019 | if (!iscsi_is_session_dev(dev)) | 1066 | if (!iscsi_is_session_dev(dev)) |
1020 | return 0; | 1067 | return 0; |
1021 | 1068 | ||
1022 | session = iscsi_dev_to_if_session(dev); | 1069 | session = iscsi_dev_to_session(dev); |
1023 | shost = iscsi_if_session_to_shost(session); | 1070 | shost = iscsi_session_to_shost(session); |
1024 | if (!shost->transportt) | 1071 | if (!shost->transportt) |
1025 | return 0; | 1072 | return 0; |
1026 | 1073 | ||
@@ -1031,23 +1078,21 @@ static int iscsi_session_match(struct attribute_container *cont, | |||
1031 | return &priv->session_cont.ac == cont; | 1078 | return &priv->session_cont.ac == cont; |
1032 | } | 1079 | } |
1033 | 1080 | ||
1034 | static int iscsi_is_conn_dev(const struct device *dev) | ||
1035 | { | ||
1036 | return dev->release == iscsi_if_conn_dev_release; | ||
1037 | } | ||
1038 | |||
1039 | static int iscsi_conn_match(struct attribute_container *cont, | 1081 | static int iscsi_conn_match(struct attribute_container *cont, |
1040 | struct device *dev) | 1082 | struct device *dev) |
1041 | { | 1083 | { |
1042 | struct iscsi_if_conn *conn; | 1084 | struct iscsi_cls_session *session; |
1085 | struct iscsi_cls_conn *conn; | ||
1043 | struct Scsi_Host *shost; | 1086 | struct Scsi_Host *shost; |
1044 | struct iscsi_internal *priv; | 1087 | struct iscsi_internal *priv; |
1045 | 1088 | ||
1046 | if (!iscsi_is_conn_dev(dev)) | 1089 | if (!iscsi_is_conn_dev(dev)) |
1047 | return 0; | 1090 | return 0; |
1048 | 1091 | ||
1049 | conn = iscsi_dev_to_if_conn(dev); | 1092 | conn = iscsi_dev_to_conn(dev); |
1050 | shost = conn->host; | 1093 | session = iscsi_dev_to_session(conn->dev.parent); |
1094 | shost = iscsi_session_to_shost(session); | ||
1095 | |||
1051 | if (!shost->transportt) | 1096 | if (!shost->transportt) |
1052 | return 0; | 1097 | return 0; |
1053 | 1098 | ||
@@ -1058,7 +1103,8 @@ static int iscsi_conn_match(struct attribute_container *cont, | |||
1058 | return &priv->conn_cont.ac == cont; | 1103 | return &priv->conn_cont.ac == cont; |
1059 | } | 1104 | } |
1060 | 1105 | ||
1061 | int iscsi_register_transport(struct iscsi_transport *tt) | 1106 | struct scsi_transport_template * |
1107 | iscsi_register_transport(struct iscsi_transport *tt) | ||
1062 | { | 1108 | { |
1063 | struct iscsi_internal *priv; | 1109 | struct iscsi_internal *priv; |
1064 | unsigned long flags; | 1110 | unsigned long flags; |
@@ -1068,15 +1114,14 @@ int iscsi_register_transport(struct iscsi_transport *tt) | |||
1068 | 1114 | ||
1069 | priv = iscsi_if_transport_lookup(tt); | 1115 | priv = iscsi_if_transport_lookup(tt); |
1070 | if (priv) | 1116 | if (priv) |
1071 | return -EEXIST; | 1117 | return NULL; |
1072 | 1118 | ||
1073 | priv = kmalloc(sizeof(*priv), GFP_KERNEL); | 1119 | priv = kmalloc(sizeof(*priv), GFP_KERNEL); |
1074 | if (!priv) | 1120 | if (!priv) |
1075 | return -ENOMEM; | 1121 | return NULL; |
1076 | memset(priv, 0, sizeof(*priv)); | 1122 | memset(priv, 0, sizeof(*priv)); |
1077 | INIT_LIST_HEAD(&priv->list); | 1123 | INIT_LIST_HEAD(&priv->list); |
1078 | INIT_LIST_HEAD(&priv->sessions); | 1124 | INIT_LIST_HEAD(&priv->sessions); |
1079 | spin_lock_init(&priv->session_lock); | ||
1080 | priv->iscsi_transport = tt; | 1125 | priv->iscsi_transport = tt; |
1081 | 1126 | ||
1082 | priv->cdev.class = &iscsi_transport_class; | 1127 | priv->cdev.class = &iscsi_transport_class; |
@@ -1142,13 +1187,13 @@ int iscsi_register_transport(struct iscsi_transport *tt) | |||
1142 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); | 1187 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
1143 | 1188 | ||
1144 | printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); | 1189 | printk(KERN_NOTICE "iscsi: registered transport (%s)\n", tt->name); |
1145 | return 0; | 1190 | return &priv->t; |
1146 | 1191 | ||
1147 | unregister_cdev: | 1192 | unregister_cdev: |
1148 | class_device_unregister(&priv->cdev); | 1193 | class_device_unregister(&priv->cdev); |
1149 | free_priv: | 1194 | free_priv: |
1150 | kfree(priv); | 1195 | kfree(priv); |
1151 | return err; | 1196 | return NULL; |
1152 | } | 1197 | } |
1153 | EXPORT_SYMBOL_GPL(iscsi_register_transport); | 1198 | EXPORT_SYMBOL_GPL(iscsi_register_transport); |
1154 | 1199 | ||
@@ -1159,19 +1204,11 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) | |||
1159 | 1204 | ||
1160 | BUG_ON(!tt); | 1205 | BUG_ON(!tt); |
1161 | 1206 | ||
1162 | down(&rx_queue_sema); | 1207 | mutex_lock(&rx_queue_mutex); |
1163 | 1208 | ||
1164 | priv = iscsi_if_transport_lookup(tt); | 1209 | priv = iscsi_if_transport_lookup(tt); |
1165 | BUG_ON (!priv); | 1210 | BUG_ON (!priv); |
1166 | 1211 | ||
1167 | spin_lock_irqsave(&priv->session_lock, flags); | ||
1168 | if (!list_empty(&priv->sessions)) { | ||
1169 | spin_unlock_irqrestore(&priv->session_lock, flags); | ||
1170 | up(&rx_queue_sema); | ||
1171 | return -EPERM; | ||
1172 | } | ||
1173 | spin_unlock_irqrestore(&priv->session_lock, flags); | ||
1174 | |||
1175 | spin_lock_irqsave(&iscsi_transport_lock, flags); | 1212 | spin_lock_irqsave(&iscsi_transport_lock, flags); |
1176 | list_del(&priv->list); | 1213 | list_del(&priv->list); |
1177 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); | 1214 | spin_unlock_irqrestore(&iscsi_transport_lock, flags); |
@@ -1181,7 +1218,7 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) | |||
1181 | 1218 | ||
1182 | sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group); | 1219 | sysfs_remove_group(&priv->cdev.kobj, &iscsi_transport_group); |
1183 | class_device_unregister(&priv->cdev); | 1220 | class_device_unregister(&priv->cdev); |
1184 | up(&rx_queue_sema); | 1221 | mutex_unlock(&rx_queue_mutex); |
1185 | 1222 | ||
1186 | return 0; | 1223 | return 0; |
1187 | } | 1224 | } |
@@ -1194,14 +1231,14 @@ iscsi_rcv_nl_event(struct notifier_block *this, unsigned long event, void *ptr) | |||
1194 | 1231 | ||
1195 | if (event == NETLINK_URELEASE && | 1232 | if (event == NETLINK_URELEASE && |
1196 | n->protocol == NETLINK_ISCSI && n->pid) { | 1233 | n->protocol == NETLINK_ISCSI && n->pid) { |
1197 | struct iscsi_if_conn *conn; | 1234 | struct iscsi_cls_conn *conn; |
1198 | unsigned long flags; | 1235 | unsigned long flags; |
1199 | 1236 | ||
1200 | mempool_zone_complete(&z_reply); | 1237 | mempool_zone_complete(z_reply); |
1201 | spin_lock_irqsave(&connlock, flags); | 1238 | spin_lock_irqsave(&connlock, flags); |
1202 | list_for_each_entry(conn, &connlist, conn_list) { | 1239 | list_for_each_entry(conn, &connlist, conn_list) { |
1203 | mempool_zone_complete(&conn->z_error); | 1240 | mempool_zone_complete(conn->z_error); |
1204 | mempool_zone_complete(&conn->z_pdu); | 1241 | mempool_zone_complete(conn->z_pdu); |
1205 | } | 1242 | } |
1206 | spin_unlock_irqrestore(&connlock, flags); | 1243 | spin_unlock_irqrestore(&connlock, flags); |
1207 | } | 1244 | } |
@@ -1234,15 +1271,15 @@ static __init int iscsi_transport_init(void) | |||
1234 | goto unregister_session_class; | 1271 | goto unregister_session_class; |
1235 | 1272 | ||
1236 | nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, | 1273 | nls = netlink_kernel_create(NETLINK_ISCSI, 1, iscsi_if_rx, |
1237 | THIS_MODULE); | 1274 | THIS_MODULE); |
1238 | if (!nls) { | 1275 | if (!nls) { |
1239 | err = -ENOBUFS; | 1276 | err = -ENOBUFS; |
1240 | goto unregister_notifier; | 1277 | goto unregister_notifier; |
1241 | } | 1278 | } |
1242 | 1279 | ||
1243 | err = mempool_zone_init(&z_reply, Z_MAX_REPLY, | 1280 | z_reply = mempool_zone_init(Z_MAX_REPLY, |
1244 | NLMSG_SPACE(sizeof(struct iscsi_uevent)), Z_HIWAT_REPLY); | 1281 | NLMSG_SPACE(sizeof(struct iscsi_uevent)), Z_HIWAT_REPLY); |
1245 | if (!err) | 1282 | if (z_reply) |
1246 | return 0; | 1283 | return 0; |
1247 | 1284 | ||
1248 | sock_release(nls->sk_socket); | 1285 | sock_release(nls->sk_socket); |
@@ -1259,7 +1296,7 @@ unregister_transport_class: | |||
1259 | 1296 | ||
1260 | static void __exit iscsi_transport_exit(void) | 1297 | static void __exit iscsi_transport_exit(void) |
1261 | { | 1298 | { |
1262 | mempool_destroy(z_reply.pool); | 1299 | mempool_zone_destroy(z_reply); |
1263 | sock_release(nls->sk_socket); | 1300 | sock_release(nls->sk_socket); |
1264 | netlink_unregister_notifier(&iscsi_nl_notifier); | 1301 | netlink_unregister_notifier(&iscsi_nl_notifier); |
1265 | transport_class_unregister(&iscsi_connection_class); | 1302 | transport_class_unregister(&iscsi_connection_class); |