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 <linux/slab.h>
  27#include <scsi/scsi.h>
  28#include <scsi/scsi_cmnd.h>
  29#include <scsi/scsi_device.h>
  30#include <scsi/scsi_host.h>
  31#include <scsi/scsi_transport.h>
  32#include <scsi/scsi_tgt.h>
  33
  34#include "scsi_tgt_priv.h"
  35
  36static struct workqueue_struct *scsi_tgtd;
  37static struct kmem_cache *scsi_tgt_cmd_cache;
  38
  39/*
  40 * TODO: this struct will be killed when the block layer supports large bios
  41 * and James's work struct code is in
  42 */
  43struct scsi_tgt_cmd {
  44        /* TODO replace work with James b's code */
  45        struct work_struct work;
  46        /* TODO fix limits of some drivers */
  47        struct bio *bio;
  48
  49        struct list_head hash_list;
  50        struct request *rq;
  51        u64 itn_id;
  52        u64 tag;
  53};
  54
  55#define TGT_HASH_ORDER  4
  56#define cmd_hashfn(tag) hash_long((unsigned long) (tag), TGT_HASH_ORDER)
  57
  58struct scsi_tgt_queuedata {
  59        struct Scsi_Host *shost;
  60        struct list_head cmd_hash[1 << TGT_HASH_ORDER];
  61        spinlock_t cmd_hash_lock;
  62};
  63
  64/*
  65 * Function:    scsi_host_get_command()
  66 *
  67 * Purpose:     Allocate and setup a scsi command block and blk request
  68 *
  69 * Arguments:   shost   - scsi host
  70 *              data_dir - dma data dir
  71 *              gfp_mask- allocator flags
  72 *
  73 * Returns:     The allocated scsi command structure.
  74 *
  75 * This should be called by target LLDs to get a command.
  76 */
  77struct scsi_cmnd *scsi_host_get_command(struct Scsi_Host *shost,
  78                                        enum dma_data_direction data_dir,
  79                                        gfp_t gfp_mask)
  80{
  81        int write = (data_dir == DMA_TO_DEVICE);
  82        struct request *rq;
  83        struct scsi_cmnd *cmd;
  84        struct scsi_tgt_cmd *tcmd;
  85
  86        /* Bail if we can't get a reference to the device */
  87        if (!get_device(&shost->shost_gendev))
  88                return NULL;
  89
  90        tcmd = kmem_cache_alloc(scsi_tgt_cmd_cache, GFP_ATOMIC);
  91        if (!tcmd)
  92                goto put_dev;
  93
  94        /*
  95         * The blk helpers are used to the READ/WRITE requests
  96         * transferring data from a initiator point of view. Since
  97         * we are in target mode we want the opposite.
  98         */
  99        rq = blk_get_request(shost->uspace_req_q, !write, gfp_mask);
 100        if (!rq)
 101                goto free_tcmd;
 102
 103        cmd = __scsi_get_command(shost, gfp_mask);
 104        if (!cmd)
 105                goto release_rq;
 106
 107        cmd->sc_data_direction = data_dir;
 108        cmd->jiffies_at_alloc = jiffies;
 109        cmd->request = rq;
 110
 111        cmd->cmnd = rq->cmd;
 112
 113        rq->special = cmd;
 114        rq->cmd_type = REQ_TYPE_SPECIAL;
 115        rq->cmd_flags |= REQ_TYPE_BLOCK_PC;
 116        rq->end_io_data = tcmd;
 117
 118        tcmd->rq = rq;
 119
 120        return cmd;
 121
 122release_rq:
 123        blk_put_request(rq);
 124free_tcmd:
 125        kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
 126put_dev:
 127        put_device(&shost->shost_gendev);
 128        return NULL;
 129
 130}
 131EXPORT_SYMBOL_GPL(scsi_host_get_command);
 132
 133/*
 134 * Function:    scsi_host_put_command()
 135 *
 136 * Purpose:     Free a scsi command block
 137 *
 138 * Arguments:   shost   - scsi host
 139 *              cmd     - command block to free
 140 *
 141 * Returns:     Nothing.
 142 *
 143 * Notes:       The command must not belong to any lists.
 144 */
 145void scsi_host_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
 146{
 147        struct request_queue *q = shost->uspace_req_q;
 148        struct request *rq = cmd->request;
 149        struct scsi_tgt_cmd *tcmd = rq->end_io_data;
 150        unsigned long flags;
 151
 152        kmem_cache_free(scsi_tgt_cmd_cache, tcmd);
 153
 154        spin_lock_irqsave(q->queue_lock, flags);
 155        __blk_put_request(q, rq);
 156        spin_unlock_irqrestore(q->queue_lock, flags);
 157
 158        __scsi_put_command(shost, cmd, &shost->shost_gendev);
 159}
 160EXPORT_SYMBOL_GPL(scsi_host_put_command);
 161
 162static void cmd_hashlist_del(struct scsi_cmnd *cmd)
 163{
 164        struct request_queue *q = cmd->request->q;
 165        struct scsi_tgt_queuedata *qdata = q->queuedata;
 166        unsigned long flags;
 167        struct scsi_tgt_cmd *tcmd = cmd->request->end_io_data;
 168
 169        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 170        list_del(&tcmd->hash_list);
 171        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 172}
 173
 174static void scsi_unmap_user_pages(struct scsi_tgt_cmd *tcmd)
 175{
 176        blk_rq_unmap_user(tcmd->bio);
 177}
 178
 179static void scsi_tgt_cmd_destroy(struct work_struct *work)
 180{
 181        struct scsi_tgt_cmd *tcmd =
 182                container_of(work, struct scsi_tgt_cmd, work);
 183        struct scsi_cmnd *cmd = tcmd->rq->special;
 184
 185        dprintk("cmd %p %d %u\n", cmd, cmd->sc_data_direction,
 186                rq_data_dir(cmd->request));
 187        scsi_unmap_user_pages(tcmd);
 188        tcmd->rq->bio = NULL;
 189        scsi_host_put_command(scsi_tgt_cmd_to_host(cmd), cmd);
 190}
 191
 192static void init_scsi_tgt_cmd(struct request *rq, struct scsi_tgt_cmd *tcmd,
 193                              u64 itn_id, u64 tag)
 194{
 195        struct scsi_tgt_queuedata *qdata = rq->q->queuedata;
 196        unsigned long flags;
 197        struct list_head *head;
 198
 199        tcmd->itn_id = itn_id;
 200        tcmd->tag = tag;
 201        tcmd->bio = NULL;
 202        INIT_WORK(&tcmd->work, scsi_tgt_cmd_destroy);
 203        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 204        head = &qdata->cmd_hash[cmd_hashfn(tag)];
 205        list_add(&tcmd->hash_list, head);
 206        spin_unlock_irqrestore(&qdata->cmd_hash_lock, flags);
 207}
 208
 209/*
 210 * scsi_tgt_alloc_queue - setup queue used for message passing
 211 * shost: scsi host
 212 *
 213 * This should be called by the LLD after host allocation.
 214 * And will be released when the host is released.
 215 */
 216int scsi_tgt_alloc_queue(struct Scsi_Host *shost)
 217{
 218        struct scsi_tgt_queuedata *queuedata;
 219        struct request_queue *q;
 220        int err, i;
 221
 222        /*
 223         * Do we need to send a netlink event or should uspace
 224         * just respond to the hotplug event?
 225         */
 226        q = __scsi_alloc_queue(shost, NULL);
 227        if (!q)
 228                return -ENOMEM;
 229
 230        queuedata = kzalloc(sizeof(*queuedata), GFP_KERNEL);
 231        if (!queuedata) {
 232                err = -ENOMEM;
 233                goto cleanup_queue;
 234        }
 235        queuedata->shost = shost;
 236        q->queuedata = queuedata;
 237
 238        /*
 239         * this is a silly hack. We should probably just queue as many
 240         * command as is recvd to userspace. uspace can then make
 241         * sure we do not overload the HBA
 242         */
 243        q->nr_requests = shost->can_queue;
 244        /*
 245         * We currently only support software LLDs so this does
 246         * not matter for now. Do we need this for the cards we support?
 247         * If so we should make it a host template value.
 248         */
 249        blk_queue_dma_alignment(q, 0);
 250        shost->uspace_req_q = q;
 251
 252        for (i = 0; i < ARRAY_SIZE(queuedata->cmd_hash); i++)
 253                INIT_LIST_HEAD(&queuedata->cmd_hash[i]);
 254        spin_lock_init(&queuedata->cmd_hash_lock);
 255
 256        return 0;
 257
 258cleanup_queue:
 259        blk_cleanup_queue(q);
 260        return err;
 261}
 262EXPORT_SYMBOL_GPL(scsi_tgt_alloc_queue);
 263
 264void scsi_tgt_free_queue(struct Scsi_Host *shost)
 265{
 266        int i;
 267        unsigned long flags;
 268        struct request_queue *q = shost->uspace_req_q;
 269        struct scsi_cmnd *cmd;
 270        struct scsi_tgt_queuedata *qdata = q->queuedata;
 271        struct scsi_tgt_cmd *tcmd, *n;
 272        LIST_HEAD(cmds);
 273
 274        spin_lock_irqsave(&qdata->cmd_hash_lock, flags);
 275
 276        for (i = 0; i < ARRAY_SIZE(qdata->cmd_hash); i++) {
 277                list_for_each_entry_safe(tcmd, n, &qdata->cmd_hash[i],
 278                                         hash_list)
 279                        list_move(&tcmd->hash_list, &cmds);
 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 = alloc_workqueue("scsi_tgtd", 0, 1);
 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
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.