linux/drivers/scsi/scsi_tgt_lib.c
<<
>>
Prefs
   1/*
   2 * SCSI target lib functions
   3 *
   4 * Copyright (C) 2005 Mike Christie <michaelc@cs.wisc.edu>
   5 * Copyright (C) 2005 FUJITA Tomonori <tomof@acm.org>
   6 *
   7 * This program is free software; you can redistribute it and/or
   8 * modify it under the terms of the GNU General Public License as
   9 * published by the Free Software Foundation; either version 2 of the
  10 * License, or (at your option) any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20 * 02110-1301 USA
  21 */
  22#include <linux/blkdev.h>
  23#include <linux/hash.h>
  24#include <linux/module.h>
  25#include <linux/pagemap.h>
  26#include <scsi/scsi.h>
  27#include <scsi/scsi_cmnd.h>
  28#include <scsi/scsi_device.h>
  29#include <scsi/scsi_host.h>
  30#include <scsi/scsi_transport.h>
  31#include <scsi/scsi_tgt.h>
  32
  33#include "scsi_tgt_priv.h"
  34
  35static struct workqueue_struct *scsi_tgtd;
  36static struct kmem_cache *scsi_tgt_cmd_cache;
  37
  38/*
  39 * TODO: this struct will be killed when the block layer supports large bios
  40 * and James's work struct code is in
  41 */
  42struct scsi_tgt_cmd {
  43        /* TODO replace work with James b's code */
  44        struct work_struct work;
  45        /* TODO fix limits of some drivers */
  46        struct bio *bio;
  47
  48        struct list_head hash_list;
  49        struct request *rq;
  50        u64 itn_id;
  51        u64 tag;
  52};
  53
  54#define TGT_HASH_ORDER  4
  55#define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER)
  56
  57struct scsi_tgt_queuedata {
  58        struct Scsi_Host *shost;
  59        struct list_head cmd_hash[1 << TGT_HASH_ORDER];
  60        spinlock_t cmd_hash_lock;
  61};
  62
  63/*
  64 * Function:    scsi_host_get_command()
  65 *
  66 * Purpose:     Allocate and setup a scsi command block and blk request
  67 *
  68 * Arguments:   shost   - scsi host
  69 *              data_dir - dma data dir
  70 *              gfp_mask- allocator flags
  71 *
  72 * Returns:     The allocated scsi command structure.
  73 *
  74 * This should be called by target LLDs to get a command.
  75 */
  76struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost,
  77                                        enum dma_data_direction data_dir,
  78                                        gfp_t gfp_mask)
  79{
  80        int write = (data_dir == DMA_TO_DEVICE);
  81        struct request *rq;
  82        struct scsi_cmnd *cmd;
  83        struct scsi_tgt_cmd *tcmd;
  84
  85        /* Bail if we can't get a reference to the device */
  86        if (!get_device(&shost->shost_gendev))
  87                return NULL;
  88
  89        tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC);
  90        if (!tcmd)
  91                goto put_dev;
  92
  93        /*
  94         * The blk helpers are used to the READ/WRITE requests
  95         * transfering data from a initiator point of view. Since
  96         * we are in target mode we want the opposite.
  97         */
  98        rq = blk_get_request(shost->uspace_req_q, !write, gfp_mask);
  99        if (!rq)
 100                goto free_tcmd;
 101
 102        cmd = __scsi_get_command(shost, gfp_mask);
 103        if (!cmd)
 104                goto release_rq;
 105
 106        cmd->sc_data_direction = data_dir;
 107        cmd->jiffies_at_alloc = jiffies;
 108        cmd->request = rq;
 109
 110        cmd->cmnd = rq->cmd;
 111
 112        rq->special = cmd;
 113        rq->cmd_type = REQ_TYPE_SPECIAL;
 114        rq->cmd_flags |= REQ_TYPE_BLOCK_PC;
 115        rq->end_io_data = tcmd;
 116
 117        tcmd->rq = rq;
 118
 119        return cmd;
 120
 121release_rq:
 122        blk_put_request(rq);
 123free_tcmd:
 124        kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
 125put_dev:
 126        put_device(&shost->shost_gendev);
 127        return NULL;
 128
 129}
 130EXPORT_SYMBOL_GPL(scsi_host_get_command);
 131
 132/*
 133 * Function:    scsi_host_put_command()
 134 *
 135 * Purpose:     Free a scsi command block
 136 *
 137 * Arguments:   shost   - scsi host
 138 *              cmd     - command block to free
 139 *
 140 * Returns:     Nothing.
 141 *
 142 * Notes:       The command must not belong to any lists.
 143 */
 144void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
 145{
 146        struct request_queue *q = shost->uspace_req_q;
 147        struct request *rq = cmd->request;
 148        struct scsi_tgt_cmd *tcmd = rq->end_io_data;
 149        unsigned long flags;
 150
 151        kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
 152
 153        spin_lock_irqsave(q->queue_lock, flags);
 154        __blk_put_request(q, rq);
 155        spin_unlock_irqrestore(q->queue_lock, flags);
 156
 157        __scsi_put_command(shost, cmd, &shost->shost_gendev);
 158}
 159EXPORT_SYMBOL_GPL(scsi_host_put_command);
 160
 161static void cmd_hashlist_del(struct scsi_cmnd *cmd)
 162{
 163        struct request_queue *q = cmd->request->q;
 164        struct scsi_tgt_queuedata *qdata = q->queuedata;
 165        unsigned long flags;
 166        struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
 167
 168        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 169        list_del(&tcmd->hash_list);
 170        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 171}
 172
 173static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd)
 174{
 175        blk_rq_unmap_user(tcmd->bio);
 176}
 177
 178static void scsi_tgt_cmd_destroy(struct work_struct *work)
 179{
 180        struct scsi_tgt_cmd *tcmd =
 181                container_of(work, struct scsi_tgt_cmd, work);
 182        struct scsi_cmnd *cmd = tcmd->rq->special;
 183
 184        dprintk("cmd %p %d %u\n", cmd, cmd->sc_data_direction,
 185                rq_data_dir(cmd->request));
 186        scsi_unmap_user_pages(tcmd);
 187        scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd);
 188}
 189
 190static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd,
 191                              u64 itn_id, u64 tag)
 192{
 193        struct scsi_tgt_queuedata *qdata = rq->q->queuedata;
 194        unsigned long flags;
 195        struct list_head *head;
 196
 197        tcmd->itn_id = itn_id;
 198        tcmd->tag = tag;
 199        tcmd->bio = NULL;
 200        INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy);
 201        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 202        head = &qdata->cmd_hash[cmd_hashfn(tag)];
 203        list_add(&tcmd->hash_list, head);
 204        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 205}
 206
 207/*
 208 * scsi_tgt_alloc_queue - setup queue used for message passing
 209 * shost: scsi host
 210 *
 211 * This should be called by the LLD after host allocation.
 212 * And will be released when the host is released.
 213 */
 214int scsi_tgt_alloc_queue(struct Scsi_Host *shost)
 215{
 216        struct scsi_tgt_queuedata *queuedata;
 217        struct request_queue *q;
 218        int err, i;
 219
 220        /*
 221         * Do we need to send a netlink event or should uspace
 222         * just respond to the hotplug event?
 223         */
 224        q = __scsi_alloc_queue(shost, NULL);
 225        if (!q)
 226                return -ENOMEM;
 227
 228        queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL);
 229        if (!queuedata) {
 230                err = -ENOMEM;
 231                goto cleanup_queue;
 232        }
 233        queuedata->shost = shost;
 234        q->queuedata = queuedata;
 235
 236        /*
 237         * this is a silly hack. We should probably just queue as many
 238         * command as is recvd to userspace. uspace can then make
 239         * sure we do not overload the HBA
 240         */
 241        q->nr_requests = shost->can_queue;
 242        /*
 243         * We currently only support software LLDs so this does
 244         * not matter for now. Do we need this for the cards we support?
 245         * If so we should make it a host template value.
 246         */
 247        blk_queue_dma_alignment(q, 0);
 248        shost->uspace_req_q = q;
 249
 250        for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++)
 251                INIT_LIST_HEAD(&queuedata->cmd_hash[i]);
 252        spin_lock_init(&queuedata->cmd_hash_lock);
 253
 254        return 0;
 255
 256cleanup_queue:
 257        blk_cleanup_queue(q);
 258        return err;
 259}
 260EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue);
 261
 262void scsi_tgt_free_queue(struct Scsi_Host *shost)
 263{
 264        int i;
 265        unsigned long flags;
 266        struct request_queue *q = shost->uspace_req_q;
 267        struct scsi_cmnd *cmd;
 268        struct scsi_tgt_queuedata *qdata = q->queuedata;
 269        struct scsi_tgt_cmd *tcmd, *n;
 270        LIST_HEAD(cmds);
 271
 272        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 273
 274        for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) {
 275                list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i],
 276                                         hash_list) {
 277                        list_del(&tcmd->hash_list);
 278                        list_add(&tcmd->hash_list, &cmds);
 279                }
 280        }
 281
 282        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 283
 284        while (!list_empty(&cmds)) {
 285                tcmd = list_entry(cmds.next, struct scsi_tgt_cmd, hash_list);
 286                list_del(&tcmd->hash_list);
 287                cmd = tcmd->rq->special;
 288
 289                shost->hostt->eh_abort_handler(cmd);
 290                scsi_tgt_cmd_destroy(&tcmd->work);
 291        }
 292}
 293EXPORT_SYMBOL_GPL(scsi_tgt_free_queue);
 294
 295struct Scsi_Host *scsi_tgt_cmd_to_host(struct scsi_cmnd *cmd)
 296{
 297        struct scsi_tgt_queuedata *queue = cmd->request->q->queuedata;
 298        return queue->shost;
 299}
 300EXPORT_SYMBOL_GPL(scsi_tgt_cmd_to_host);
 301
 302/*
 303 * scsi_tgt_queue_command - queue command for userspace processing
 304 * @cmd:        scsi command
 305 * @scsilun:    scsi lun
 306 * @tag:        unique value to identify this command for tmf
 307 */
 308int scsi_tgt_queue_command(struct scsi_cmnd *cmd, u64 itn_id,
 309                           struct scsi_lun *scsilun, u64 tag)
 310{
 311        struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
 312        int err;
 313
 314        init_scsi_tgt_cmd(cmd->request, tcmd, itn_id, tag);
 315        err = scsi_tgt_uspace_send_cmd(cmd, itn_id, scsilun, tag);
 316        if (err)
 317                cmd_hashlist_del(cmd);
 318
 319        return err;
 320}
 321EXPORT_SYMBOL_GPL(scsi_tgt_queue_command);
 322
 323/*
 324 * This is run from a interrupt handler normally and the unmap
 325 * needs process context so we must queue
 326 */
 327static void scsi_tgt_cmd_done(struct scsi_cmnd *cmd)
 328{
 329        struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
 330
 331        dprintk("cmd %p %u\n", cmd, rq_data_dir(cmd->request));
 332
 333        scsi_tgt_uspace_send_status(cmd, tcmd->itn_id, tcmd->tag);
 334
 335        scsi_release_buffers(cmd);
 336
 337        queue_work(scsi_tgtd, &tcmd->work);
 338}
 339
 340static int scsi_tgt_transfer_response(struct scsi_cmnd *cmd)
 341{
 342        struct Scsi_Host *shost = scsi_tgt_cmd_to_host(cmd);
 343        int err;
 344
 345        dprintk("cmd %p %u\n", cmd, rq_data_dir(cmd->request));
 346
 347        err = shost->hostt->transfer_response(cmd, scsi_tgt_cmd_done);
 348        switch (err) {
 349        case SCSI_MLQUEUE_HOST_BUSY:
 350        case SCSI_MLQUEUE_DEVICE_BUSY:
 351                return -EAGAIN;
 352        }
 353        return 0;
 354}
 355
 356/* TODO: test this crap and replace bio_map_user with new interface maybe */
 357static int scsi_map_user_pages(struct scsi_tgt_cmd *tcmd, struct scsi_cmnd *cmd,
 358                               unsigned long uaddr, unsigned int len, int rw)
 359{
 360        struct request_queue *q = cmd->request->q;
 361        struct request *rq = cmd->request;
 362        int err;
 363
 364        dprintk("%lx %u\n", uaddr, len);
 365        err = blk_rq_map_user(q, rq, NULL, (void *)uaddr, len, GFP_KERNEL);
 366        if (err) {
 367                /*
 368                 * TODO: need to fixup sg_tablesize, max_segment_size,
 369                 * max_sectors, etc for modern HW and software drivers
 370                 * where this value is bogus.
 371                 *
 372                 * TODO2: we can alloc a reserve buffer of max size
 373                 * we can handle and do the slow copy path for really large
 374                 * IO.
 375                 */
 376                eprintk("Could not handle request of size %u.\n", len);
 377                return err;
 378        }
 379
 380        tcmd->bio = rq->bio;
 381        err = scsi_init_io(cmd, GFP_KERNEL);
 382        if (err) {
 383                scsi_release_buffers(cmd);
 384                goto unmap_rq;
 385        }
 386        /*
 387         * we use REQ_TYPE_BLOCK_PC so scsi_init_io doesn't set the
 388         * length for us.
 389         */
 390        cmd->sdb.length = blk_rq_bytes(rq);
 391
 392        return 0;
 393
 394unmap_rq:
 395        scsi_unmap_user_pages(tcmd);
 396        return err;
 397}
 398
 399static int scsi_tgt_copy_sense(struct scsi_cmnd *cmd, unsigned long uaddr,
 400                                unsigned len)
 401{
 402        char __user *p = (char __user *) uaddr;
 403
 404        if (copy_from_user(cmd->sense_buffer, p,
 405                           min_t(unsigned, SCSI_SENSE_BUFFERSIZE, len))) {
 406                printk(KERN_ERR "Could not copy the sense buffer\n");
 407                return -EIO;
 408        }
 409        return 0;
 410}
 411
 412static int scsi_tgt_abort_cmd(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
 413{
 414        struct scsi_tgt_cmd *tcmd;
 415        int err;
 416
 417        err = shost->hostt->eh_abort_handler(cmd);
 418        if (err)
 419                eprintk("fail to abort %p\n", cmd);
 420
 421        tcmd = cmd->request->end_io_data;
 422        scsi_tgt_cmd_destroy(&tcmd->work);
 423        return err;
 424}
 425
 426static struct request *tgt_cmd_hash_lookup(struct request_queue *q, u64 tag)
 427{
 428        struct scsi_tgt_queuedata *qdata = q->queuedata;
 429        struct request *rq = NULL;
 430        struct list_head *head;
 431        struct scsi_tgt_cmd *tcmd;
 432        unsigned long flags;
 433
 434        head = &qdata->cmd_hash[cmd_hashfn(tag)];
 435        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 436        list_for_each_entry(tcmd, head, hash_list) {
 437                if (tcmd->tag == tag) {
 438                        rq = tcmd->rq;
 439                        list_del(&tcmd->hash_list);
 440                        break;
 441                }
 442        }
 443        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 444
 445        return rq;
 446}
 447
 448int scsi_tgt_kspace_exec(int host_no, u64 itn_id, int result, u64 tag,
 449                         unsigned long uaddr, u32 len, unsigned long sense_uaddr,
 450                         u32 sense_len, u8 rw)
 451{
 452        struct Scsi_Host *shost;
 453        struct scsi_cmnd *cmd;
 454        struct request *rq;
 455        struct scsi_tgt_cmd *tcmd;
 456        int err = 0;
 457
 458        dprintk("%d %llu %d %u %lx %u\n", host_no, (unsigned long long) tag,
 459                result, len, uaddr, rw);
 460
 461        /* TODO: replace with a O(1) alg */
 462        shost = scsi_host_lookup(host_no);
 463        if (!shost) {
 464                printk(KERN_ERR "Could not find host no %d\n", host_no);
 465                return -EINVAL;
 466        }
 467
 468        if (!shost->uspace_req_q) {
 469                printk(KERN_ERR "Not target scsi host %d\n", host_no);
 470                goto done;
 471        }
 472
 473        rq = tgt_cmd_hash_lookup(shost->uspace_req_q, tag);
 474        if (!rq) {
 475                printk(KERN_ERR "Could not find tag %llu\n",
 476                       (unsigned long long) tag);
 477                err = -EINVAL;
 478                goto done;
 479        }
 480        cmd = rq->special;
 481
 482        dprintk("cmd %p scb %x result %d len %d bufflen %u %u %x\n",
 483                cmd, cmd->cmnd[0], result, len, scsi_bufflen(cmd),
 484                rq_data_dir(rq), cmd->cmnd[0]);
 485
 486        if (result == TASK_ABORTED) {
 487                scsi_tgt_abort_cmd(shost, cmd);
 488                goto done;
 489        }
 490        /*
 491         * store the userspace values here, the working values are
 492         * in the request_* values
 493         */
 494        tcmd = cmd->request->end_io_data;
 495        cmd->result = result;
 496
 497        if (cmd->result == SAM_STAT_CHECK_CONDITION)
 498                scsi_tgt_copy_sense(cmd, sense_uaddr, sense_len);
 499
 500        if (len) {
 501                err = scsi_map_user_pages(rq->end_io_data, cmd, uaddr, len, rw);
 502                if (err) {
 503                        /*
 504                         * user-space daemon bugs or OOM
 505                         * TODO: we can do better for OOM.
 506                         */
 507                        struct scsi_tgt_queuedata *qdata;
 508                        struct list_head *head;
 509                        unsigned long flags;
 510
 511                        eprintk("cmd %p ret %d uaddr %lx len %d rw %d\n",
 512                                cmd, err, uaddr, len, rw);
 513
 514                        qdata = shost->uspace_req_q->queuedata;
 515                        head = &qdata->cmd_hash[cmd_hashfn(tcmd->tag)];
 516
 517                        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 518                        list_add(&tcmd->hash_list, head);
 519                        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 520
 521                        goto done;
 522                }
 523        }
 524        err = scsi_tgt_transfer_response(cmd);
 525done:
 526        scsi_host_put(shost);
 527        return err;
 528}
 529
 530int scsi_tgt_tsk_mgmt_request(struct Scsi_Host *shost, u64 itn_id,
 531                              int function, u64 tag, struct scsi_lun *scsilun,
 532                              void *data)
 533{
 534        int err;
 535
 536        /* TODO: need to retry if this fails. */
 537        err = scsi_tgt_uspace_send_tsk_mgmt(shost->host_no, itn_id,
 538                                            function, tag, scsilun, data);
 539        if (err < 0)
 540                eprintk("The task management request lost!\n");
 541        return err;
 542}
 543EXPORT_SYMBOL_GPL(scsi_tgt_tsk_mgmt_request);
 544
 545int scsi_tgt_kspace_tsk_mgmt(int host_no, u64 itn_id, u64 mid, int result)
 546{
 547        struct Scsi_Host *shost;
 548        int err = -EINVAL;
 549
 550        dprintk("%d %d %llx\n", host_no, result, (unsigned long long) mid);
 551
 552        shost = scsi_host_lookup(host_no);
 553        if (!shost) {
 554                printk(KERN_ERR "Could not find host no %d\n", host_no);
 555                return err;
 556        }
 557
 558        if (!shost->uspace_req_q) {
 559                printk(KERN_ERR "Not target scsi host %d\n", host_no);
 560                goto done;
 561        }
 562
 563        err = shost->transportt->tsk_mgmt_response(shost, itn_id, mid, result);
 564done:
 565        scsi_host_put(shost);
 566        return err;
 567}
 568
 569int scsi_tgt_it_nexus_create(struct Scsi_Host *shost, u64 itn_id,
 570                             char *initiator)
 571{
 572        int err;
 573
 574        /* TODO: need to retry if this fails. */
 575        err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no, itn_id, 0,
 576                                                    initiator);
 577        if (err < 0)
 578                eprintk("The i_t_neuxs request lost, %d %llx!\n",
 579                        shost->host_no, (unsigned long long)itn_id);
 580        return err;
 581}
 582EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_create);
 583
 584int scsi_tgt_it_nexus_destroy(struct Scsi_Host *shost, u64 itn_id)
 585{
 586        int err;
 587
 588        /* TODO: need to retry if this fails. */
 589        err = scsi_tgt_uspace_send_it_nexus_request(shost->host_no,
 590                                                    itn_id, 1, NULL);
 591        if (err < 0)
 592                eprintk("The i_t_neuxs request lost, %d %llx!\n",
 593                        shost->host_no, (unsigned long long)itn_id);
 594        return err;
 595}
 596EXPORT_SYMBOL_GPL(scsi_tgt_it_nexus_destroy);
 597
 598int scsi_tgt_kspace_it_nexus_rsp(int host_no, u64 itn_id, int result)
 599{
 600        struct Scsi_Host *shost;
 601        int err = -EINVAL;
 602
 603        dprintk("%d %d%llx\n", host_no, result, (unsigned long long)itn_id);
 604
 605        shost = scsi_host_lookup(host_no);
 606        if (!shost) {
 607                printk(KERN_ERR "Could not find host no %d\n", host_no);
 608                return err;
 609        }
 610
 611        if (!shost->uspace_req_q) {
 612                printk(KERN_ERR "Not target scsi host %d\n", host_no);
 613                goto done;
 614        }
 615
 616        err = shost->transportt->it_nexus_response(shost, itn_id, result);
 617done:
 618        scsi_host_put(shost);
 619        return err;
 620}
 621
 622static int __init scsi_tgt_init(void)
 623{
 624        int err;
 625
 626        scsi_tgt_cmd_cache =  KMEM_CACHE(scsi_tgt_cmd, 0);
 627        if (!scsi_tgt_cmd_cache)
 628                return -ENOMEM;
 629
 630        scsi_tgtd = create_workqueue("scsi_tgtd");
 631        if (!scsi_tgtd) {
 632                err = -ENOMEM;
 633                goto free_kmemcache;
 634        }
 635
 636        err = scsi_tgt_if_init();
 637        if (err)
 638                goto destroy_wq;
 639
 640        return 0;
 641
 642destroy_wq:
 643        destroy_workqueue(scsi_tgtd);
 644free_kmemcache:
 645        kmem_cache_destroy(scsi_tgt_cmd_cache);
 646        return err;
 647}
 648
 649static void __exit scsi_tgt_exit(void)
 650{
 651        destroy_workqueue(scsi_tgtd);
 652        scsi_tgt_if_exit();
 653        kmem_cache_destroy(scsi_tgt_cmd_cache);
 654}
 655
 656module_init(scsi_tgt_init);
 657module_exit(scsi_tgt_exit);
 658
 659MODULE_DESCRIPTION("SCSI target core");
 660MODULE_LICENSE("GPL");
 661