123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920 |
- /*
- * Xen SCSI backend driver
- *
- * Copyright (c) 2008, FUJITSU Limited
- *
- * Based on the blkback driver code.
- * Adaption to kernel taget core infrastructure taken from vhost/scsi.c
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2
- * as published by the Free Software Foundation; or, when distributed
- * separately from the Linux kernel or incorporated into other
- * software packages, subject to the following license:
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this source file (the "Software"), to deal in the Software without
- * restriction, including without limitation the rights to use, copy, modify,
- * merge, publish, distribute, sublicense, and/or sell copies of the Software,
- * and to permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- * IN THE SOFTWARE.
- */
- #define pr_fmt(fmt) "xen-pvscsi: " fmt
- #include <stdarg.h>
- #include <linux/module.h>
- #include <linux/utsname.h>
- #include <linux/interrupt.h>
- #include <linux/slab.h>
- #include <linux/wait.h>
- #include <linux/sched.h>
- #include <linux/list.h>
- #include <linux/gfp.h>
- #include <linux/delay.h>
- #include <linux/spinlock.h>
- #include <linux/configfs.h>
- #include <generated/utsrelease.h>
- #include <scsi/scsi_host.h> /* SG_ALL */
- #include <target/target_core_base.h>
- #include <target/target_core_fabric.h>
- #include <asm/hypervisor.h>
- #include <xen/xen.h>
- #include <xen/balloon.h>
- #include <xen/events.h>
- #include <xen/xenbus.h>
- #include <xen/grant_table.h>
- #include <xen/page.h>
- #include <xen/interface/grant_table.h>
- #include <xen/interface/io/vscsiif.h>
- #define VSCSI_VERSION "v0.1"
- #define VSCSI_NAMELEN 32
- struct ids_tuple {
- unsigned int hst; /* host */
- unsigned int chn; /* channel */
- unsigned int tgt; /* target */
- unsigned int lun; /* LUN */
- };
- struct v2p_entry {
- struct ids_tuple v; /* translate from */
- struct scsiback_tpg *tpg; /* translate to */
- unsigned int lun;
- struct kref kref;
- struct list_head l;
- };
- struct vscsibk_info {
- struct xenbus_device *dev;
- domid_t domid;
- unsigned int irq;
- struct vscsiif_back_ring ring;
- int ring_error;
- spinlock_t ring_lock;
- atomic_t nr_unreplied_reqs;
- spinlock_t v2p_lock;
- struct list_head v2p_entry_lists;
- wait_queue_head_t waiting_to_free;
- };
- /* theoretical maximum of grants for one request */
- #define VSCSI_MAX_GRANTS (SG_ALL + VSCSIIF_SG_TABLESIZE)
- /*
- * VSCSI_GRANT_BATCH is the maximum number of grants to be processed in one
- * call to map/unmap grants. Don't choose it too large, as there are arrays
- * with VSCSI_GRANT_BATCH elements allocated on the stack.
- */
- #define VSCSI_GRANT_BATCH 16
- struct vscsibk_pend {
- uint16_t rqid;
- uint8_t cmnd[VSCSIIF_MAX_COMMAND_SIZE];
- uint8_t cmd_len;
- uint8_t sc_data_direction;
- uint16_t n_sg; /* real length of SG list */
- uint16_t n_grants; /* SG pages and potentially SG list */
- uint32_t data_len;
- uint32_t result;
- struct vscsibk_info *info;
- struct v2p_entry *v2p;
- struct scatterlist *sgl;
- uint8_t sense_buffer[VSCSIIF_SENSE_BUFFERSIZE];
- grant_handle_t grant_handles[VSCSI_MAX_GRANTS];
- struct page *pages[VSCSI_MAX_GRANTS];
- struct se_cmd se_cmd;
- };
- struct scsiback_tmr {
- atomic_t tmr_complete;
- wait_queue_head_t tmr_wait;
- };
- struct scsiback_nexus {
- /* Pointer to TCM session for I_T Nexus */
- struct se_session *tvn_se_sess;
- };
- struct scsiback_tport {
- /* SCSI protocol the tport is providing */
- u8 tport_proto_id;
- /* Binary World Wide unique Port Name for pvscsi Target port */
- u64 tport_wwpn;
- /* ASCII formatted WWPN for pvscsi Target port */
- char tport_name[VSCSI_NAMELEN];
- /* Returned by scsiback_make_tport() */
- struct se_wwn tport_wwn;
- };
- struct scsiback_tpg {
- /* scsiback port target portal group tag for TCM */
- u16 tport_tpgt;
- /* track number of TPG Port/Lun Links wrt explicit I_T Nexus shutdown */
- int tv_tpg_port_count;
- /* xen-pvscsi references to tpg_nexus, protected by tv_tpg_mutex */
- int tv_tpg_fe_count;
- /* list for scsiback_list */
- struct list_head tv_tpg_list;
- /* Used to protect access for tpg_nexus */
- struct mutex tv_tpg_mutex;
- /* Pointer to the TCM pvscsi I_T Nexus for this TPG endpoint */
- struct scsiback_nexus *tpg_nexus;
- /* Pointer back to scsiback_tport */
- struct scsiback_tport *tport;
- /* Returned by scsiback_make_tpg() */
- struct se_portal_group se_tpg;
- /* alias used in xenstore */
- char param_alias[VSCSI_NAMELEN];
- /* list of info structures related to this target portal group */
- struct list_head info_list;
- };
- #define SCSIBACK_INVALID_HANDLE (~0)
- static bool log_print_stat;
- module_param(log_print_stat, bool, 0644);
- static int scsiback_max_buffer_pages = 1024;
- module_param_named(max_buffer_pages, scsiback_max_buffer_pages, int, 0644);
- MODULE_PARM_DESC(max_buffer_pages,
- "Maximum number of free pages to keep in backend buffer");
- static struct kmem_cache *scsiback_cachep;
- static DEFINE_SPINLOCK(free_pages_lock);
- static int free_pages_num;
- static LIST_HEAD(scsiback_free_pages);
- /* Global spinlock to protect scsiback TPG list */
- static DEFINE_MUTEX(scsiback_mutex);
- static LIST_HEAD(scsiback_list);
- static void scsiback_get(struct vscsibk_info *info)
- {
- atomic_inc(&info->nr_unreplied_reqs);
- }
- static void scsiback_put(struct vscsibk_info *info)
- {
- if (atomic_dec_and_test(&info->nr_unreplied_reqs))
- wake_up(&info->waiting_to_free);
- }
- static void put_free_pages(struct page **page, int num)
- {
- unsigned long flags;
- int i = free_pages_num + num, n = num;
- if (num == 0)
- return;
- if (i > scsiback_max_buffer_pages) {
- n = min(num, i - scsiback_max_buffer_pages);
- gnttab_free_pages(n, page + num - n);
- n = num - n;
- }
- spin_lock_irqsave(&free_pages_lock, flags);
- for (i = 0; i < n; i++)
- list_add(&page[i]->lru, &scsiback_free_pages);
- free_pages_num += n;
- spin_unlock_irqrestore(&free_pages_lock, flags);
- }
- static int get_free_page(struct page **page)
- {
- unsigned long flags;
- spin_lock_irqsave(&free_pages_lock, flags);
- if (list_empty(&scsiback_free_pages)) {
- spin_unlock_irqrestore(&free_pages_lock, flags);
- return gnttab_alloc_pages(1, page);
- }
- page[0] = list_first_entry(&scsiback_free_pages, struct page, lru);
- list_del(&page[0]->lru);
- free_pages_num--;
- spin_unlock_irqrestore(&free_pages_lock, flags);
- return 0;
- }
- static unsigned long vaddr_page(struct page *page)
- {
- unsigned long pfn = page_to_pfn(page);
- return (unsigned long)pfn_to_kaddr(pfn);
- }
- static unsigned long vaddr(struct vscsibk_pend *req, int seg)
- {
- return vaddr_page(req->pages[seg]);
- }
- static void scsiback_print_status(char *sense_buffer, int errors,
- struct vscsibk_pend *pending_req)
- {
- struct scsiback_tpg *tpg = pending_req->v2p->tpg;
- pr_err("[%s:%d] cmnd[0]=%02x -> st=%02x msg=%02x host=%02x drv=%02x\n",
- tpg->tport->tport_name, pending_req->v2p->lun,
- pending_req->cmnd[0], status_byte(errors), msg_byte(errors),
- host_byte(errors), driver_byte(errors));
- }
- static void scsiback_fast_flush_area(struct vscsibk_pend *req)
- {
- struct gnttab_unmap_grant_ref unmap[VSCSI_GRANT_BATCH];
- struct page *pages[VSCSI_GRANT_BATCH];
- unsigned int i, invcount = 0;
- grant_handle_t handle;
- int err;
- kfree(req->sgl);
- req->sgl = NULL;
- req->n_sg = 0;
- if (!req->n_grants)
- return;
- for (i = 0; i < req->n_grants; i++) {
- handle = req->grant_handles[i];
- if (handle == SCSIBACK_INVALID_HANDLE)
- continue;
- gnttab_set_unmap_op(&unmap[invcount], vaddr(req, i),
- GNTMAP_host_map, handle);
- req->grant_handles[i] = SCSIBACK_INVALID_HANDLE;
- pages[invcount] = req->pages[i];
- put_page(pages[invcount]);
- invcount++;
- if (invcount < VSCSI_GRANT_BATCH)
- continue;
- err = gnttab_unmap_refs(unmap, NULL, pages, invcount);
- BUG_ON(err);
- invcount = 0;
- }
- if (invcount) {
- err = gnttab_unmap_refs(unmap, NULL, pages, invcount);
- BUG_ON(err);
- }
- put_free_pages(req->pages, req->n_grants);
- req->n_grants = 0;
- }
- static void scsiback_free_translation_entry(struct kref *kref)
- {
- struct v2p_entry *entry = container_of(kref, struct v2p_entry, kref);
- struct scsiback_tpg *tpg = entry->tpg;
- mutex_lock(&tpg->tv_tpg_mutex);
- tpg->tv_tpg_fe_count--;
- mutex_unlock(&tpg->tv_tpg_mutex);
- kfree(entry);
- }
- static void scsiback_do_resp_with_sense(char *sense_buffer, int32_t result,
- uint32_t resid, struct vscsibk_pend *pending_req)
- {
- struct vscsiif_response *ring_res;
- struct vscsibk_info *info = pending_req->info;
- int notify;
- struct scsi_sense_hdr sshdr;
- unsigned long flags;
- unsigned len;
- spin_lock_irqsave(&info->ring_lock, flags);
- ring_res = RING_GET_RESPONSE(&info->ring, info->ring.rsp_prod_pvt);
- info->ring.rsp_prod_pvt++;
- ring_res->rslt = result;
- ring_res->rqid = pending_req->rqid;
- if (sense_buffer != NULL &&
- scsi_normalize_sense(sense_buffer, VSCSIIF_SENSE_BUFFERSIZE,
- &sshdr)) {
- len = min_t(unsigned, 8 + sense_buffer[7],
- VSCSIIF_SENSE_BUFFERSIZE);
- memcpy(ring_res->sense_buffer, sense_buffer, len);
- ring_res->sense_len = len;
- } else {
- ring_res->sense_len = 0;
- }
- ring_res->residual_len = resid;
- RING_PUSH_RESPONSES_AND_CHECK_NOTIFY(&info->ring, notify);
- spin_unlock_irqrestore(&info->ring_lock, flags);
- if (notify)
- notify_remote_via_irq(info->irq);
- if (pending_req->v2p)
- kref_put(&pending_req->v2p->kref,
- scsiback_free_translation_entry);
- }
- static void scsiback_cmd_done(struct vscsibk_pend *pending_req)
- {
- struct vscsibk_info *info = pending_req->info;
- unsigned char *sense_buffer;
- unsigned int resid;
- int errors;
- sense_buffer = pending_req->sense_buffer;
- resid = pending_req->se_cmd.residual_count;
- errors = pending_req->result;
- if (errors && log_print_stat)
- scsiback_print_status(sense_buffer, errors, pending_req);
- scsiback_fast_flush_area(pending_req);
- scsiback_do_resp_with_sense(sense_buffer, errors, resid, pending_req);
- scsiback_put(info);
- }
- static void scsiback_cmd_exec(struct vscsibk_pend *pending_req)
- {
- struct se_cmd *se_cmd = &pending_req->se_cmd;
- struct se_session *sess = pending_req->v2p->tpg->tpg_nexus->tvn_se_sess;
- int rc;
- memset(pending_req->sense_buffer, 0, VSCSIIF_SENSE_BUFFERSIZE);
- memset(se_cmd, 0, sizeof(*se_cmd));
- scsiback_get(pending_req->info);
- se_cmd->tag = pending_req->rqid;
- rc = target_submit_cmd_map_sgls(se_cmd, sess, pending_req->cmnd,
- pending_req->sense_buffer, pending_req->v2p->lun,
- pending_req->data_len, 0,
- pending_req->sc_data_direction, 0,
- pending_req->sgl, pending_req->n_sg,
- NULL, 0, NULL, 0);
- if (rc < 0) {
- transport_send_check_condition_and_sense(se_cmd,
- TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE, 0);
- transport_generic_free_cmd(se_cmd, 0);
- }
- }
- static int scsiback_gnttab_data_map_batch(struct gnttab_map_grant_ref *map,
- struct page **pg, grant_handle_t *grant, int cnt)
- {
- int err, i;
- if (!cnt)
- return 0;
- err = gnttab_map_refs(map, NULL, pg, cnt);
- BUG_ON(err);
- for (i = 0; i < cnt; i++) {
- if (unlikely(map[i].status != GNTST_okay)) {
- pr_err("invalid buffer -- could not remap it\n");
- map[i].handle = SCSIBACK_INVALID_HANDLE;
- err = -ENOMEM;
- } else {
- get_page(pg[i]);
- }
- grant[i] = map[i].handle;
- }
- return err;
- }
- static int scsiback_gnttab_data_map_list(struct vscsibk_pend *pending_req,
- struct scsiif_request_segment *seg, struct page **pg,
- grant_handle_t *grant, int cnt, u32 flags)
- {
- int mapcount = 0, i, err = 0;
- struct gnttab_map_grant_ref map[VSCSI_GRANT_BATCH];
- struct vscsibk_info *info = pending_req->info;
- for (i = 0; i < cnt; i++) {
- if (get_free_page(pg + mapcount)) {
- put_free_pages(pg, mapcount);
- pr_err("no grant page\n");
- return -ENOMEM;
- }
- gnttab_set_map_op(&map[mapcount], vaddr_page(pg[mapcount]),
- flags, seg[i].gref, info->domid);
- mapcount++;
- if (mapcount < VSCSI_GRANT_BATCH)
- continue;
- err = scsiback_gnttab_data_map_batch(map, pg, grant, mapcount);
- pg += mapcount;
- grant += mapcount;
- pending_req->n_grants += mapcount;
- if (err)
- return err;
- mapcount = 0;
- }
- err = scsiback_gnttab_data_map_batch(map, pg, grant, mapcount);
- pending_req->n_grants += mapcount;
- return err;
- }
- static int scsiback_gnttab_data_map(struct vscsiif_request *ring_req,
- struct vscsibk_pend *pending_req)
- {
- u32 flags;
- int i, err, n_segs, i_seg = 0;
- struct page **pg;
- struct scsiif_request_segment *seg;
- unsigned long end_seg = 0;
- unsigned int nr_segments = (unsigned int)ring_req->nr_segments;
- unsigned int nr_sgl = 0;
- struct scatterlist *sg;
- grant_handle_t *grant;
- pending_req->n_sg = 0;
- pending_req->n_grants = 0;
- pending_req->data_len = 0;
- nr_segments &= ~VSCSIIF_SG_GRANT;
- if (!nr_segments)
- return 0;
- if (nr_segments > VSCSIIF_SG_TABLESIZE) {
- pr_debug("invalid parameter nr_seg = %d\n",
- ring_req->nr_segments);
- return -EINVAL;
- }
- if (ring_req->nr_segments & VSCSIIF_SG_GRANT) {
- err = scsiback_gnttab_data_map_list(pending_req, ring_req->seg,
- pending_req->pages, pending_req->grant_handles,
- nr_segments, GNTMAP_host_map | GNTMAP_readonly);
- if (err)
- return err;
- nr_sgl = nr_segments;
- nr_segments = 0;
- for (i = 0; i < nr_sgl; i++) {
- n_segs = ring_req->seg[i].length /
- sizeof(struct scsiif_request_segment);
- if ((unsigned)ring_req->seg[i].offset +
- (unsigned)ring_req->seg[i].length > PAGE_SIZE ||
- n_segs * sizeof(struct scsiif_request_segment) !=
- ring_req->seg[i].length)
- return -EINVAL;
- nr_segments += n_segs;
- }
- if (nr_segments > SG_ALL) {
- pr_debug("invalid nr_seg = %d\n", nr_segments);
- return -EINVAL;
- }
- }
- /* free of (sgl) in fast_flush_area() */
- pending_req->sgl = kmalloc_array(nr_segments,
- sizeof(struct scatterlist), GFP_KERNEL);
- if (!pending_req->sgl)
- return -ENOMEM;
- sg_init_table(pending_req->sgl, nr_segments);
- pending_req->n_sg = nr_segments;
- flags = GNTMAP_host_map;
- if (pending_req->sc_data_direction == DMA_TO_DEVICE)
- flags |= GNTMAP_readonly;
- pg = pending_req->pages + nr_sgl;
- grant = pending_req->grant_handles + nr_sgl;
- if (!nr_sgl) {
- seg = ring_req->seg;
- err = scsiback_gnttab_data_map_list(pending_req, seg,
- pg, grant, nr_segments, flags);
- if (err)
- return err;
- } else {
- for (i = 0; i < nr_sgl; i++) {
- seg = (struct scsiif_request_segment *)(
- vaddr(pending_req, i) + ring_req->seg[i].offset);
- n_segs = ring_req->seg[i].length /
- sizeof(struct scsiif_request_segment);
- err = scsiback_gnttab_data_map_list(pending_req, seg,
- pg, grant, n_segs, flags);
- if (err)
- return err;
- pg += n_segs;
- grant += n_segs;
- }
- end_seg = vaddr(pending_req, 0) + ring_req->seg[0].offset;
- seg = (struct scsiif_request_segment *)end_seg;
- end_seg += ring_req->seg[0].length;
- pg = pending_req->pages + nr_sgl;
- }
- for_each_sg(pending_req->sgl, sg, nr_segments, i) {
- sg_set_page(sg, pg[i], seg->length, seg->offset);
- pending_req->data_len += seg->length;
- seg++;
- if (nr_sgl && (unsigned long)seg >= end_seg) {
- i_seg++;
- end_seg = vaddr(pending_req, i_seg) +
- ring_req->seg[i_seg].offset;
- seg = (struct scsiif_request_segment *)end_seg;
- end_seg += ring_req->seg[i_seg].length;
- }
- if (sg->offset >= PAGE_SIZE ||
- sg->length > PAGE_SIZE ||
- sg->offset + sg->length > PAGE_SIZE)
- return -EINVAL;
- }
- return 0;
- }
- static void scsiback_disconnect(struct vscsibk_info *info)
- {
- wait_event(info->waiting_to_free,
- atomic_read(&info->nr_unreplied_reqs) == 0);
- unbind_from_irqhandler(info->irq, info);
- info->irq = 0;
- xenbus_unmap_ring_vfree(info->dev, info->ring.sring);
- }
- static void scsiback_device_action(struct vscsibk_pend *pending_req,
- enum tcm_tmreq_table act, int tag)
- {
- int rc, err = FAILED;
- struct scsiback_tpg *tpg = pending_req->v2p->tpg;
- struct se_cmd *se_cmd = &pending_req->se_cmd;
- struct scsiback_tmr *tmr;
- tmr = kzalloc(sizeof(struct scsiback_tmr), GFP_KERNEL);
- if (!tmr)
- goto out;
- init_waitqueue_head(&tmr->tmr_wait);
- transport_init_se_cmd(se_cmd, tpg->se_tpg.se_tpg_tfo,
- tpg->tpg_nexus->tvn_se_sess, 0, DMA_NONE, TCM_SIMPLE_TAG,
- &pending_req->sense_buffer[0]);
- rc = core_tmr_alloc_req(se_cmd, tmr, act, GFP_KERNEL);
- if (rc < 0)
- goto out;
- se_cmd->se_tmr_req->ref_task_tag = tag;
- if (transport_lookup_tmr_lun(se_cmd, pending_req->v2p->lun) < 0)
- goto out;
- transport_generic_handle_tmr(se_cmd);
- wait_event(tmr->tmr_wait, atomic_read(&tmr->tmr_complete));
- err = (se_cmd->se_tmr_req->response == TMR_FUNCTION_COMPLETE) ?
- SUCCESS : FAILED;
- out:
- if (tmr) {
- transport_generic_free_cmd(&pending_req->se_cmd, 1);
- kfree(tmr);
- }
- scsiback_do_resp_with_sense(NULL, err, 0, pending_req);
- kmem_cache_free(scsiback_cachep, pending_req);
- }
- /*
- Perform virtual to physical translation
- */
- static struct v2p_entry *scsiback_do_translation(struct vscsibk_info *info,
- struct ids_tuple *v)
- {
- struct v2p_entry *entry;
- struct list_head *head = &(info->v2p_entry_lists);
- unsigned long flags;
- spin_lock_irqsave(&info->v2p_lock, flags);
- list_for_each_entry(entry, head, l) {
- if ((entry->v.chn == v->chn) &&
- (entry->v.tgt == v->tgt) &&
- (entry->v.lun == v->lun)) {
- kref_get(&entry->kref);
- goto out;
- }
- }
- entry = NULL;
- out:
- spin_unlock_irqrestore(&info->v2p_lock, flags);
- return entry;
- }
- static int prepare_pending_reqs(struct vscsibk_info *info,
- struct vscsiif_request *ring_req,
- struct vscsibk_pend *pending_req)
- {
- struct v2p_entry *v2p;
- struct ids_tuple vir;
- pending_req->rqid = ring_req->rqid;
- pending_req->info = info;
- vir.chn = ring_req->channel;
- vir.tgt = ring_req->id;
- vir.lun = ring_req->lun;
- v2p = scsiback_do_translation(info, &vir);
- if (!v2p) {
- pending_req->v2p = NULL;
- pr_debug("the v2p of (chn:%d, tgt:%d, lun:%d) doesn't exist.\n",
- vir.chn, vir.tgt, vir.lun);
- return -ENODEV;
- }
- pending_req->v2p = v2p;
- /* request range check from frontend */
- pending_req->sc_data_direction = ring_req->sc_data_direction;
- if ((pending_req->sc_data_direction != DMA_BIDIRECTIONAL) &&
- (pending_req->sc_data_direction != DMA_TO_DEVICE) &&
- (pending_req->sc_data_direction != DMA_FROM_DEVICE) &&
- (pending_req->sc_data_direction != DMA_NONE)) {
- pr_debug("invalid parameter data_dir = %d\n",
- pending_req->sc_data_direction);
- return -EINVAL;
- }
- pending_req->cmd_len = ring_req->cmd_len;
- if (pending_req->cmd_len > VSCSIIF_MAX_COMMAND_SIZE) {
- pr_debug("invalid parameter cmd_len = %d\n",
- pending_req->cmd_len);
- return -EINVAL;
- }
- memcpy(pending_req->cmnd, ring_req->cmnd, pending_req->cmd_len);
- return 0;
- }
- static int scsiback_do_cmd_fn(struct vscsibk_info *info)
- {
- struct vscsiif_back_ring *ring = &info->ring;
- struct vscsiif_request ring_req;
- struct vscsibk_pend *pending_req;
- RING_IDX rc, rp;
- int err, more_to_do;
- uint32_t result;
- rc = ring->req_cons;
- rp = ring->sring->req_prod;
- rmb(); /* guest system is accessing ring, too */
- if (RING_REQUEST_PROD_OVERFLOW(ring, rp)) {
- rc = ring->rsp_prod_pvt;
- pr_warn("Dom%d provided bogus ring requests (%#x - %#x = %u). Halting ring processing\n",
- info->domid, rp, rc, rp - rc);
- info->ring_error = 1;
- return 0;
- }
- while ((rc != rp)) {
- if (RING_REQUEST_CONS_OVERFLOW(ring, rc))
- break;
- pending_req = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL);
- if (!pending_req)
- return 1;
- RING_COPY_REQUEST(ring, rc, &ring_req);
- ring->req_cons = ++rc;
- err = prepare_pending_reqs(info, &ring_req, pending_req);
- if (err) {
- switch (err) {
- case -ENODEV:
- result = DID_NO_CONNECT;
- break;
- default:
- result = DRIVER_ERROR;
- break;
- }
- scsiback_do_resp_with_sense(NULL, result << 24, 0,
- pending_req);
- kmem_cache_free(scsiback_cachep, pending_req);
- return 1;
- }
- switch (ring_req.act) {
- case VSCSIIF_ACT_SCSI_CDB:
- if (scsiback_gnttab_data_map(&ring_req, pending_req)) {
- scsiback_fast_flush_area(pending_req);
- scsiback_do_resp_with_sense(NULL,
- DRIVER_ERROR << 24, 0, pending_req);
- kmem_cache_free(scsiback_cachep, pending_req);
- } else {
- scsiback_cmd_exec(pending_req);
- }
- break;
- case VSCSIIF_ACT_SCSI_ABORT:
- scsiback_device_action(pending_req, TMR_ABORT_TASK,
- ring_req.ref_rqid);
- break;
- case VSCSIIF_ACT_SCSI_RESET:
- scsiback_device_action(pending_req, TMR_LUN_RESET, 0);
- break;
- default:
- pr_err_ratelimited("invalid request\n");
- scsiback_do_resp_with_sense(NULL, DRIVER_ERROR << 24,
- 0, pending_req);
- kmem_cache_free(scsiback_cachep, pending_req);
- break;
- }
- /* Yield point for this unbounded loop. */
- cond_resched();
- }
- RING_FINAL_CHECK_FOR_REQUESTS(&info->ring, more_to_do);
- return more_to_do;
- }
- static irqreturn_t scsiback_irq_fn(int irq, void *dev_id)
- {
- struct vscsibk_info *info = dev_id;
- if (info->ring_error)
- return IRQ_HANDLED;
- while (scsiback_do_cmd_fn(info))
- cond_resched();
- return IRQ_HANDLED;
- }
- static int scsiback_init_sring(struct vscsibk_info *info, grant_ref_t ring_ref,
- evtchn_port_t evtchn)
- {
- void *area;
- struct vscsiif_sring *sring;
- int err;
- if (info->irq)
- return -1;
- err = xenbus_map_ring_valloc(info->dev, &ring_ref, 1, &area);
- if (err)
- return err;
- sring = (struct vscsiif_sring *)area;
- BACK_RING_INIT(&info->ring, sring, PAGE_SIZE);
- err = bind_interdomain_evtchn_to_irq(info->domid, evtchn);
- if (err < 0)
- goto unmap_page;
- info->irq = err;
- err = request_threaded_irq(info->irq, NULL, scsiback_irq_fn,
- IRQF_ONESHOT, "vscsiif-backend", info);
- if (err)
- goto free_irq;
- return 0;
- free_irq:
- unbind_from_irqhandler(info->irq, info);
- info->irq = 0;
- unmap_page:
- xenbus_unmap_ring_vfree(info->dev, area);
- return err;
- }
- static int scsiback_map(struct vscsibk_info *info)
- {
- struct xenbus_device *dev = info->dev;
- unsigned int ring_ref, evtchn;
- int err;
- err = xenbus_gather(XBT_NIL, dev->otherend,
- "ring-ref", "%u", &ring_ref,
- "event-channel", "%u", &evtchn, NULL);
- if (err) {
- xenbus_dev_fatal(dev, err, "reading %s ring", dev->otherend);
- return err;
- }
- return scsiback_init_sring(info, ring_ref, evtchn);
- }
- /*
- Add a new translation entry
- */
- static int scsiback_add_translation_entry(struct vscsibk_info *info,
- char *phy, struct ids_tuple *v)
- {
- int err = 0;
- struct v2p_entry *entry;
- struct v2p_entry *new;
- struct list_head *head = &(info->v2p_entry_lists);
- unsigned long flags;
- char *lunp;
- unsigned long long unpacked_lun;
- struct se_lun *se_lun;
- struct scsiback_tpg *tpg_entry, *tpg = NULL;
- char *error = "doesn't exist";
- lunp = strrchr(phy, ':');
- if (!lunp) {
- pr_err("illegal format of physical device %s\n", phy);
- return -EINVAL;
- }
- *lunp = 0;
- lunp++;
- err = kstrtoull(lunp, 10, &unpacked_lun);
- if (err < 0) {
- pr_err("lun number not valid: %s\n", lunp);
- return err;
- }
- mutex_lock(&scsiback_mutex);
- list_for_each_entry(tpg_entry, &scsiback_list, tv_tpg_list) {
- if (!strcmp(phy, tpg_entry->tport->tport_name) ||
- !strcmp(phy, tpg_entry->param_alias)) {
- mutex_lock(&tpg_entry->se_tpg.tpg_lun_mutex);
- hlist_for_each_entry(se_lun, &tpg_entry->se_tpg.tpg_lun_hlist, link) {
- if (se_lun->unpacked_lun == unpacked_lun) {
- if (!tpg_entry->tpg_nexus)
- error = "nexus undefined";
- else
- tpg = tpg_entry;
- break;
- }
- }
- mutex_unlock(&tpg_entry->se_tpg.tpg_lun_mutex);
- break;
- }
- }
- if (tpg) {
- mutex_lock(&tpg->tv_tpg_mutex);
- tpg->tv_tpg_fe_count++;
- mutex_unlock(&tpg->tv_tpg_mutex);
- }
- mutex_unlock(&scsiback_mutex);
- if (!tpg) {
- pr_err("%s:%llu %s\n", phy, unpacked_lun, error);
- return -ENODEV;
- }
- new = kmalloc(sizeof(struct v2p_entry), GFP_KERNEL);
- if (new == NULL) {
- err = -ENOMEM;
- goto out_free;
- }
- spin_lock_irqsave(&info->v2p_lock, flags);
- /* Check double assignment to identical virtual ID */
- list_for_each_entry(entry, head, l) {
- if ((entry->v.chn == v->chn) &&
- (entry->v.tgt == v->tgt) &&
- (entry->v.lun == v->lun)) {
- pr_warn("Virtual ID is already used. Assignment was not performed.\n");
- err = -EEXIST;
- goto out;
- }
- }
- /* Create a new translation entry and add to the list */
- kref_init(&new->kref);
- new->v = *v;
- new->tpg = tpg;
- new->lun = unpacked_lun;
- list_add_tail(&new->l, head);
- out:
- spin_unlock_irqrestore(&info->v2p_lock, flags);
- out_free:
- if (err) {
- mutex_lock(&tpg->tv_tpg_mutex);
- tpg->tv_tpg_fe_count--;
- mutex_unlock(&tpg->tv_tpg_mutex);
- kfree(new);
- }
- return err;
- }
- static void __scsiback_del_translation_entry(struct v2p_entry *entry)
- {
- list_del(&entry->l);
- kref_put(&entry->kref, scsiback_free_translation_entry);
- }
- /*
- Delete the translation entry specfied
- */
- static int scsiback_del_translation_entry(struct vscsibk_info *info,
- struct ids_tuple *v)
- {
- struct v2p_entry *entry;
- struct list_head *head = &(info->v2p_entry_lists);
- unsigned long flags;
- spin_lock_irqsave(&info->v2p_lock, flags);
- /* Find out the translation entry specified */
- list_for_each_entry(entry, head, l) {
- if ((entry->v.chn == v->chn) &&
- (entry->v.tgt == v->tgt) &&
- (entry->v.lun == v->lun)) {
- goto found;
- }
- }
- spin_unlock_irqrestore(&info->v2p_lock, flags);
- return 1;
- found:
- /* Delete the translation entry specfied */
- __scsiback_del_translation_entry(entry);
- spin_unlock_irqrestore(&info->v2p_lock, flags);
- return 0;
- }
- static void scsiback_do_add_lun(struct vscsibk_info *info, const char *state,
- char *phy, struct ids_tuple *vir, int try)
- {
- if (!scsiback_add_translation_entry(info, phy, vir)) {
- if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
- "%d", XenbusStateInitialised)) {
- pr_err("xenbus_printf error %s\n", state);
- scsiback_del_translation_entry(info, vir);
- }
- } else if (!try) {
- xenbus_printf(XBT_NIL, info->dev->nodename, state,
- "%d", XenbusStateClosed);
- }
- }
- static void scsiback_do_del_lun(struct vscsibk_info *info, const char *state,
- struct ids_tuple *vir)
- {
- if (!scsiback_del_translation_entry(info, vir)) {
- if (xenbus_printf(XBT_NIL, info->dev->nodename, state,
- "%d", XenbusStateClosed))
- pr_err("xenbus_printf error %s\n", state);
- }
- }
- #define VSCSIBACK_OP_ADD_OR_DEL_LUN 1
- #define VSCSIBACK_OP_UPDATEDEV_STATE 2
- static void scsiback_do_1lun_hotplug(struct vscsibk_info *info, int op,
- char *ent)
- {
- int err;
- struct ids_tuple vir;
- char *val;
- int device_state;
- char phy[VSCSI_NAMELEN];
- char str[64];
- char state[64];
- struct xenbus_device *dev = info->dev;
- /* read status */
- snprintf(state, sizeof(state), "vscsi-devs/%s/state", ent);
- err = xenbus_scanf(XBT_NIL, dev->nodename, state, "%u", &device_state);
- if (XENBUS_EXIST_ERR(err))
- return;
- /* physical SCSI device */
- snprintf(str, sizeof(str), "vscsi-devs/%s/p-dev", ent);
- val = xenbus_read(XBT_NIL, dev->nodename, str, NULL);
- if (IS_ERR(val)) {
- xenbus_printf(XBT_NIL, dev->nodename, state,
- "%d", XenbusStateClosed);
- return;
- }
- strlcpy(phy, val, VSCSI_NAMELEN);
- kfree(val);
- /* virtual SCSI device */
- snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", ent);
- err = xenbus_scanf(XBT_NIL, dev->nodename, str, "%u:%u:%u:%u",
- &vir.hst, &vir.chn, &vir.tgt, &vir.lun);
- if (XENBUS_EXIST_ERR(err)) {
- xenbus_printf(XBT_NIL, dev->nodename, state,
- "%d", XenbusStateClosed);
- return;
- }
- switch (op) {
- case VSCSIBACK_OP_ADD_OR_DEL_LUN:
- switch (device_state) {
- case XenbusStateInitialising:
- scsiback_do_add_lun(info, state, phy, &vir, 0);
- break;
- case XenbusStateConnected:
- scsiback_do_add_lun(info, state, phy, &vir, 1);
- break;
- case XenbusStateClosing:
- scsiback_do_del_lun(info, state, &vir);
- break;
- default:
- break;
- }
- break;
- case VSCSIBACK_OP_UPDATEDEV_STATE:
- if (device_state == XenbusStateInitialised) {
- /* modify vscsi-devs/dev-x/state */
- if (xenbus_printf(XBT_NIL, dev->nodename, state,
- "%d", XenbusStateConnected)) {
- pr_err("xenbus_printf error %s\n", str);
- scsiback_del_translation_entry(info, &vir);
- xenbus_printf(XBT_NIL, dev->nodename, state,
- "%d", XenbusStateClosed);
- }
- }
- break;
- /* When it is necessary, processing is added here. */
- default:
- break;
- }
- }
- static void scsiback_do_lun_hotplug(struct vscsibk_info *info, int op)
- {
- int i;
- char **dir;
- unsigned int ndir = 0;
- dir = xenbus_directory(XBT_NIL, info->dev->nodename, "vscsi-devs",
- &ndir);
- if (IS_ERR(dir))
- return;
- for (i = 0; i < ndir; i++)
- scsiback_do_1lun_hotplug(info, op, dir[i]);
- kfree(dir);
- }
- static void scsiback_frontend_changed(struct xenbus_device *dev,
- enum xenbus_state frontend_state)
- {
- struct vscsibk_info *info = dev_get_drvdata(&dev->dev);
- switch (frontend_state) {
- case XenbusStateInitialising:
- break;
- case XenbusStateInitialised:
- if (scsiback_map(info))
- break;
- scsiback_do_lun_hotplug(info, VSCSIBACK_OP_ADD_OR_DEL_LUN);
- xenbus_switch_state(dev, XenbusStateConnected);
- break;
- case XenbusStateConnected:
- scsiback_do_lun_hotplug(info, VSCSIBACK_OP_UPDATEDEV_STATE);
- if (dev->state == XenbusStateConnected)
- break;
- xenbus_switch_state(dev, XenbusStateConnected);
- break;
- case XenbusStateClosing:
- if (info->irq)
- scsiback_disconnect(info);
- xenbus_switch_state(dev, XenbusStateClosing);
- break;
- case XenbusStateClosed:
- xenbus_switch_state(dev, XenbusStateClosed);
- if (xenbus_dev_is_online(dev))
- break;
- /* fall through if not online */
- case XenbusStateUnknown:
- device_unregister(&dev->dev);
- break;
- case XenbusStateReconfiguring:
- scsiback_do_lun_hotplug(info, VSCSIBACK_OP_ADD_OR_DEL_LUN);
- xenbus_switch_state(dev, XenbusStateReconfigured);
- break;
- default:
- xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
- frontend_state);
- break;
- }
- }
- /*
- Release the translation entry specfied
- */
- static void scsiback_release_translation_entry(struct vscsibk_info *info)
- {
- struct v2p_entry *entry, *tmp;
- struct list_head *head = &(info->v2p_entry_lists);
- unsigned long flags;
- spin_lock_irqsave(&info->v2p_lock, flags);
- list_for_each_entry_safe(entry, tmp, head, l)
- __scsiback_del_translation_entry(entry);
- spin_unlock_irqrestore(&info->v2p_lock, flags);
- }
- static int scsiback_remove(struct xenbus_device *dev)
- {
- struct vscsibk_info *info = dev_get_drvdata(&dev->dev);
- if (info->irq)
- scsiback_disconnect(info);
- scsiback_release_translation_entry(info);
- dev_set_drvdata(&dev->dev, NULL);
- return 0;
- }
- static int scsiback_probe(struct xenbus_device *dev,
- const struct xenbus_device_id *id)
- {
- int err;
- struct vscsibk_info *info = kzalloc(sizeof(struct vscsibk_info),
- GFP_KERNEL);
- pr_debug("%s %p %d\n", __func__, dev, dev->otherend_id);
- if (!info) {
- xenbus_dev_fatal(dev, -ENOMEM, "allocating backend structure");
- return -ENOMEM;
- }
- info->dev = dev;
- dev_set_drvdata(&dev->dev, info);
- info->domid = dev->otherend_id;
- spin_lock_init(&info->ring_lock);
- info->ring_error = 0;
- atomic_set(&info->nr_unreplied_reqs, 0);
- init_waitqueue_head(&info->waiting_to_free);
- info->dev = dev;
- info->irq = 0;
- INIT_LIST_HEAD(&info->v2p_entry_lists);
- spin_lock_init(&info->v2p_lock);
- err = xenbus_printf(XBT_NIL, dev->nodename, "feature-sg-grant", "%u",
- SG_ALL);
- if (err)
- xenbus_dev_error(dev, err, "writing feature-sg-grant");
- err = xenbus_switch_state(dev, XenbusStateInitWait);
- if (err)
- goto fail;
- return 0;
- fail:
- pr_warn("%s failed\n", __func__);
- scsiback_remove(dev);
- return err;
- }
- static char *scsiback_dump_proto_id(struct scsiback_tport *tport)
- {
- switch (tport->tport_proto_id) {
- case SCSI_PROTOCOL_SAS:
- return "SAS";
- case SCSI_PROTOCOL_FCP:
- return "FCP";
- case SCSI_PROTOCOL_ISCSI:
- return "iSCSI";
- default:
- break;
- }
- return "Unknown";
- }
- static char *scsiback_get_fabric_wwn(struct se_portal_group *se_tpg)
- {
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- struct scsiback_tport *tport = tpg->tport;
- return &tport->tport_name[0];
- }
- static u16 scsiback_get_tag(struct se_portal_group *se_tpg)
- {
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- return tpg->tport_tpgt;
- }
- static struct se_wwn *
- scsiback_make_tport(struct target_fabric_configfs *tf,
- struct config_group *group,
- const char *name)
- {
- struct scsiback_tport *tport;
- char *ptr;
- u64 wwpn = 0;
- int off = 0;
- tport = kzalloc(sizeof(struct scsiback_tport), GFP_KERNEL);
- if (!tport)
- return ERR_PTR(-ENOMEM);
- tport->tport_wwpn = wwpn;
- /*
- * Determine the emulated Protocol Identifier and Target Port Name
- * based on the incoming configfs directory name.
- */
- ptr = strstr(name, "naa.");
- if (ptr) {
- tport->tport_proto_id = SCSI_PROTOCOL_SAS;
- goto check_len;
- }
- ptr = strstr(name, "fc.");
- if (ptr) {
- tport->tport_proto_id = SCSI_PROTOCOL_FCP;
- off = 3; /* Skip over "fc." */
- goto check_len;
- }
- ptr = strstr(name, "iqn.");
- if (ptr) {
- tport->tport_proto_id = SCSI_PROTOCOL_ISCSI;
- goto check_len;
- }
- pr_err("Unable to locate prefix for emulated Target Port: %s\n", name);
- kfree(tport);
- return ERR_PTR(-EINVAL);
- check_len:
- if (strlen(name) >= VSCSI_NAMELEN) {
- pr_err("Emulated %s Address: %s, exceeds max: %d\n", name,
- scsiback_dump_proto_id(tport), VSCSI_NAMELEN);
- kfree(tport);
- return ERR_PTR(-EINVAL);
- }
- snprintf(&tport->tport_name[0], VSCSI_NAMELEN, "%s", &name[off]);
- pr_debug("Allocated emulated Target %s Address: %s\n",
- scsiback_dump_proto_id(tport), name);
- return &tport->tport_wwn;
- }
- static void scsiback_drop_tport(struct se_wwn *wwn)
- {
- struct scsiback_tport *tport = container_of(wwn,
- struct scsiback_tport, tport_wwn);
- pr_debug("Deallocating emulated Target %s Address: %s\n",
- scsiback_dump_proto_id(tport), tport->tport_name);
- kfree(tport);
- }
- static u32 scsiback_tpg_get_inst_index(struct se_portal_group *se_tpg)
- {
- return 1;
- }
- static int scsiback_check_stop_free(struct se_cmd *se_cmd)
- {
- /*
- * Do not release struct se_cmd's containing a valid TMR pointer.
- * These will be released directly in scsiback_device_action()
- * with transport_generic_free_cmd().
- */
- if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB)
- return 0;
- transport_generic_free_cmd(se_cmd, 0);
- return 1;
- }
- static void scsiback_release_cmd(struct se_cmd *se_cmd)
- {
- struct vscsibk_pend *pending_req = container_of(se_cmd,
- struct vscsibk_pend, se_cmd);
- kmem_cache_free(scsiback_cachep, pending_req);
- }
- static int scsiback_shutdown_session(struct se_session *se_sess)
- {
- return 0;
- }
- static void scsiback_close_session(struct se_session *se_sess)
- {
- }
- static u32 scsiback_sess_get_index(struct se_session *se_sess)
- {
- return 0;
- }
- static int scsiback_write_pending(struct se_cmd *se_cmd)
- {
- /* Go ahead and process the write immediately */
- target_execute_cmd(se_cmd);
- return 0;
- }
- static int scsiback_write_pending_status(struct se_cmd *se_cmd)
- {
- return 0;
- }
- static void scsiback_set_default_node_attrs(struct se_node_acl *nacl)
- {
- }
- static int scsiback_get_cmd_state(struct se_cmd *se_cmd)
- {
- return 0;
- }
- static int scsiback_queue_data_in(struct se_cmd *se_cmd)
- {
- struct vscsibk_pend *pending_req = container_of(se_cmd,
- struct vscsibk_pend, se_cmd);
- pending_req->result = SAM_STAT_GOOD;
- scsiback_cmd_done(pending_req);
- return 0;
- }
- static int scsiback_queue_status(struct se_cmd *se_cmd)
- {
- struct vscsibk_pend *pending_req = container_of(se_cmd,
- struct vscsibk_pend, se_cmd);
- if (se_cmd->sense_buffer &&
- ((se_cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) ||
- (se_cmd->se_cmd_flags & SCF_EMULATED_TASK_SENSE)))
- pending_req->result = (DRIVER_SENSE << 24) |
- SAM_STAT_CHECK_CONDITION;
- else
- pending_req->result = se_cmd->scsi_status;
- scsiback_cmd_done(pending_req);
- return 0;
- }
- static void scsiback_queue_tm_rsp(struct se_cmd *se_cmd)
- {
- struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;
- struct scsiback_tmr *tmr = se_tmr->fabric_tmr_ptr;
- atomic_set(&tmr->tmr_complete, 1);
- wake_up(&tmr->tmr_wait);
- }
- static void scsiback_aborted_task(struct se_cmd *se_cmd)
- {
- }
- static ssize_t scsiback_tpg_param_alias_show(struct config_item *item,
- char *page)
- {
- struct se_portal_group *se_tpg = param_to_tpg(item);
- struct scsiback_tpg *tpg = container_of(se_tpg, struct scsiback_tpg,
- se_tpg);
- ssize_t rb;
- mutex_lock(&tpg->tv_tpg_mutex);
- rb = snprintf(page, PAGE_SIZE, "%s\n", tpg->param_alias);
- mutex_unlock(&tpg->tv_tpg_mutex);
- return rb;
- }
- static ssize_t scsiback_tpg_param_alias_store(struct config_item *item,
- const char *page, size_t count)
- {
- struct se_portal_group *se_tpg = param_to_tpg(item);
- struct scsiback_tpg *tpg = container_of(se_tpg, struct scsiback_tpg,
- se_tpg);
- int len;
- if (strlen(page) >= VSCSI_NAMELEN) {
- pr_err("param alias: %s, exceeds max: %d\n", page,
- VSCSI_NAMELEN);
- return -EINVAL;
- }
- mutex_lock(&tpg->tv_tpg_mutex);
- len = snprintf(tpg->param_alias, VSCSI_NAMELEN, "%s", page);
- if (tpg->param_alias[len - 1] == '\n')
- tpg->param_alias[len - 1] = '\0';
- mutex_unlock(&tpg->tv_tpg_mutex);
- return count;
- }
- CONFIGFS_ATTR(scsiback_tpg_param_, alias);
- static struct configfs_attribute *scsiback_param_attrs[] = {
- &scsiback_tpg_param_attr_alias,
- NULL,
- };
- static int scsiback_make_nexus(struct scsiback_tpg *tpg,
- const char *name)
- {
- struct se_portal_group *se_tpg;
- struct se_session *se_sess;
- struct scsiback_nexus *tv_nexus;
- mutex_lock(&tpg->tv_tpg_mutex);
- if (tpg->tpg_nexus) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- pr_debug("tpg->tpg_nexus already exists\n");
- return -EEXIST;
- }
- se_tpg = &tpg->se_tpg;
- tv_nexus = kzalloc(sizeof(struct scsiback_nexus), GFP_KERNEL);
- if (!tv_nexus) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- return -ENOMEM;
- }
- /*
- * Initialize the struct se_session pointer
- */
- tv_nexus->tvn_se_sess = transport_init_session(TARGET_PROT_NORMAL);
- if (IS_ERR(tv_nexus->tvn_se_sess)) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- kfree(tv_nexus);
- return -ENOMEM;
- }
- se_sess = tv_nexus->tvn_se_sess;
- /*
- * Since we are running in 'demo mode' this call with generate a
- * struct se_node_acl for the scsiback struct se_portal_group with
- * the SCSI Initiator port name of the passed configfs group 'name'.
- */
- tv_nexus->tvn_se_sess->se_node_acl = core_tpg_check_initiator_node_acl(
- se_tpg, (unsigned char *)name);
- if (!tv_nexus->tvn_se_sess->se_node_acl) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- pr_debug("core_tpg_check_initiator_node_acl() failed for %s\n",
- name);
- goto out;
- }
- /* Now register the TCM pvscsi virtual I_T Nexus as active. */
- transport_register_session(se_tpg, tv_nexus->tvn_se_sess->se_node_acl,
- tv_nexus->tvn_se_sess, tv_nexus);
- tpg->tpg_nexus = tv_nexus;
- mutex_unlock(&tpg->tv_tpg_mutex);
- return 0;
- out:
- transport_free_session(se_sess);
- kfree(tv_nexus);
- return -ENOMEM;
- }
- static int scsiback_drop_nexus(struct scsiback_tpg *tpg)
- {
- struct se_session *se_sess;
- struct scsiback_nexus *tv_nexus;
- mutex_lock(&tpg->tv_tpg_mutex);
- tv_nexus = tpg->tpg_nexus;
- if (!tv_nexus) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- return -ENODEV;
- }
- se_sess = tv_nexus->tvn_se_sess;
- if (!se_sess) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- return -ENODEV;
- }
- if (tpg->tv_tpg_port_count != 0) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- pr_err("Unable to remove xen-pvscsi I_T Nexus with active TPG port count: %d\n",
- tpg->tv_tpg_port_count);
- return -EBUSY;
- }
- if (tpg->tv_tpg_fe_count != 0) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- pr_err("Unable to remove xen-pvscsi I_T Nexus with active TPG frontend count: %d\n",
- tpg->tv_tpg_fe_count);
- return -EBUSY;
- }
- pr_debug("Removing I_T Nexus to emulated %s Initiator Port: %s\n",
- scsiback_dump_proto_id(tpg->tport),
- tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
- /*
- * Release the SCSI I_T Nexus to the emulated xen-pvscsi Target Port
- */
- transport_deregister_session(tv_nexus->tvn_se_sess);
- tpg->tpg_nexus = NULL;
- mutex_unlock(&tpg->tv_tpg_mutex);
- kfree(tv_nexus);
- return 0;
- }
- static ssize_t scsiback_tpg_nexus_show(struct config_item *item, char *page)
- {
- struct se_portal_group *se_tpg = to_tpg(item);
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- struct scsiback_nexus *tv_nexus;
- ssize_t ret;
- mutex_lock(&tpg->tv_tpg_mutex);
- tv_nexus = tpg->tpg_nexus;
- if (!tv_nexus) {
- mutex_unlock(&tpg->tv_tpg_mutex);
- return -ENODEV;
- }
- ret = snprintf(page, PAGE_SIZE, "%s\n",
- tv_nexus->tvn_se_sess->se_node_acl->initiatorname);
- mutex_unlock(&tpg->tv_tpg_mutex);
- return ret;
- }
- static ssize_t scsiback_tpg_nexus_store(struct config_item *item,
- const char *page, size_t count)
- {
- struct se_portal_group *se_tpg = to_tpg(item);
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- struct scsiback_tport *tport_wwn = tpg->tport;
- unsigned char i_port[VSCSI_NAMELEN], *ptr, *port_ptr;
- int ret;
- /*
- * Shutdown the active I_T nexus if 'NULL' is passed.
- */
- if (!strncmp(page, "NULL", 4)) {
- ret = scsiback_drop_nexus(tpg);
- return (!ret) ? count : ret;
- }
- /*
- * Otherwise make sure the passed virtual Initiator port WWN matches
- * the fabric protocol_id set in scsiback_make_tport(), and call
- * scsiback_make_nexus().
- */
- if (strlen(page) >= VSCSI_NAMELEN) {
- pr_err("Emulated NAA Sas Address: %s, exceeds max: %d\n",
- page, VSCSI_NAMELEN);
- return -EINVAL;
- }
- snprintf(&i_port[0], VSCSI_NAMELEN, "%s", page);
- ptr = strstr(i_port, "naa.");
- if (ptr) {
- if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_SAS) {
- pr_err("Passed SAS Initiator Port %s does not match target port protoid: %s\n",
- i_port, scsiback_dump_proto_id(tport_wwn));
- return -EINVAL;
- }
- port_ptr = &i_port[0];
- goto check_newline;
- }
- ptr = strstr(i_port, "fc.");
- if (ptr) {
- if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_FCP) {
- pr_err("Passed FCP Initiator Port %s does not match target port protoid: %s\n",
- i_port, scsiback_dump_proto_id(tport_wwn));
- return -EINVAL;
- }
- port_ptr = &i_port[3]; /* Skip over "fc." */
- goto check_newline;
- }
- ptr = strstr(i_port, "iqn.");
- if (ptr) {
- if (tport_wwn->tport_proto_id != SCSI_PROTOCOL_ISCSI) {
- pr_err("Passed iSCSI Initiator Port %s does not match target port protoid: %s\n",
- i_port, scsiback_dump_proto_id(tport_wwn));
- return -EINVAL;
- }
- port_ptr = &i_port[0];
- goto check_newline;
- }
- pr_err("Unable to locate prefix for emulated Initiator Port: %s\n",
- i_port);
- return -EINVAL;
- /*
- * Clear any trailing newline for the NAA WWN
- */
- check_newline:
- if (i_port[strlen(i_port) - 1] == '\n')
- i_port[strlen(i_port) - 1] = '\0';
- ret = scsiback_make_nexus(tpg, port_ptr);
- if (ret < 0)
- return ret;
- return count;
- }
- CONFIGFS_ATTR(scsiback_tpg_, nexus);
- static struct configfs_attribute *scsiback_tpg_attrs[] = {
- &scsiback_tpg_attr_nexus,
- NULL,
- };
- static ssize_t
- scsiback_wwn_version_show(struct config_item *item, char *page)
- {
- return sprintf(page, "xen-pvscsi fabric module %s on %s/%s on "
- UTS_RELEASE"\n",
- VSCSI_VERSION, utsname()->sysname, utsname()->machine);
- }
- CONFIGFS_ATTR_RO(scsiback_wwn_, version);
- static struct configfs_attribute *scsiback_wwn_attrs[] = {
- &scsiback_wwn_attr_version,
- NULL,
- };
- static char *scsiback_get_fabric_name(void)
- {
- return "xen-pvscsi";
- }
- static int scsiback_port_link(struct se_portal_group *se_tpg,
- struct se_lun *lun)
- {
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- mutex_lock(&tpg->tv_tpg_mutex);
- tpg->tv_tpg_port_count++;
- mutex_unlock(&tpg->tv_tpg_mutex);
- return 0;
- }
- static void scsiback_port_unlink(struct se_portal_group *se_tpg,
- struct se_lun *lun)
- {
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- mutex_lock(&tpg->tv_tpg_mutex);
- tpg->tv_tpg_port_count--;
- mutex_unlock(&tpg->tv_tpg_mutex);
- }
- static struct se_portal_group *
- scsiback_make_tpg(struct se_wwn *wwn,
- struct config_group *group,
- const char *name)
- {
- struct scsiback_tport *tport = container_of(wwn,
- struct scsiback_tport, tport_wwn);
- struct scsiback_tpg *tpg;
- u16 tpgt;
- int ret;
- if (strstr(name, "tpgt_") != name)
- return ERR_PTR(-EINVAL);
- ret = kstrtou16(name + 5, 10, &tpgt);
- if (ret)
- return ERR_PTR(ret);
- tpg = kzalloc(sizeof(struct scsiback_tpg), GFP_KERNEL);
- if (!tpg)
- return ERR_PTR(-ENOMEM);
- mutex_init(&tpg->tv_tpg_mutex);
- INIT_LIST_HEAD(&tpg->tv_tpg_list);
- INIT_LIST_HEAD(&tpg->info_list);
- tpg->tport = tport;
- tpg->tport_tpgt = tpgt;
- ret = core_tpg_register(wwn, &tpg->se_tpg, tport->tport_proto_id);
- if (ret < 0) {
- kfree(tpg);
- return NULL;
- }
- mutex_lock(&scsiback_mutex);
- list_add_tail(&tpg->tv_tpg_list, &scsiback_list);
- mutex_unlock(&scsiback_mutex);
- return &tpg->se_tpg;
- }
- static void scsiback_drop_tpg(struct se_portal_group *se_tpg)
- {
- struct scsiback_tpg *tpg = container_of(se_tpg,
- struct scsiback_tpg, se_tpg);
- mutex_lock(&scsiback_mutex);
- list_del(&tpg->tv_tpg_list);
- mutex_unlock(&scsiback_mutex);
- /*
- * Release the virtual I_T Nexus for this xen-pvscsi TPG
- */
- scsiback_drop_nexus(tpg);
- /*
- * Deregister the se_tpg from TCM.
- */
- core_tpg_deregister(se_tpg);
- kfree(tpg);
- }
- static int scsiback_check_true(struct se_portal_group *se_tpg)
- {
- return 1;
- }
- static int scsiback_check_false(struct se_portal_group *se_tpg)
- {
- return 0;
- }
- static const struct target_core_fabric_ops scsiback_ops = {
- .module = THIS_MODULE,
- .name = "xen-pvscsi",
- .get_fabric_name = scsiback_get_fabric_name,
- .tpg_get_wwn = scsiback_get_fabric_wwn,
- .tpg_get_tag = scsiback_get_tag,
- .tpg_check_demo_mode = scsiback_check_true,
- .tpg_check_demo_mode_cache = scsiback_check_true,
- .tpg_check_demo_mode_write_protect = scsiback_check_false,
- .tpg_check_prod_mode_write_protect = scsiback_check_false,
- .tpg_get_inst_index = scsiback_tpg_get_inst_index,
- .check_stop_free = scsiback_check_stop_free,
- .release_cmd = scsiback_release_cmd,
- .shutdown_session = scsiback_shutdown_session,
- .close_session = scsiback_close_session,
- .sess_get_index = scsiback_sess_get_index,
- .sess_get_initiator_sid = NULL,
- .write_pending = scsiback_write_pending,
- .write_pending_status = scsiback_write_pending_status,
- .set_default_node_attributes = scsiback_set_default_node_attrs,
- .get_cmd_state = scsiback_get_cmd_state,
- .queue_data_in = scsiback_queue_data_in,
- .queue_status = scsiback_queue_status,
- .queue_tm_rsp = scsiback_queue_tm_rsp,
- .aborted_task = scsiback_aborted_task,
- /*
- * Setup callers for generic logic in target_core_fabric_configfs.c
- */
- .fabric_make_wwn = scsiback_make_tport,
- .fabric_drop_wwn = scsiback_drop_tport,
- .fabric_make_tpg = scsiback_make_tpg,
- .fabric_drop_tpg = scsiback_drop_tpg,
- .fabric_post_link = scsiback_port_link,
- .fabric_pre_unlink = scsiback_port_unlink,
- .tfc_wwn_attrs = scsiback_wwn_attrs,
- .tfc_tpg_base_attrs = scsiback_tpg_attrs,
- .tfc_tpg_param_attrs = scsiback_param_attrs,
- };
- static const struct xenbus_device_id scsiback_ids[] = {
- { "vscsi" },
- { "" }
- };
- static struct xenbus_driver scsiback_driver = {
- .ids = scsiback_ids,
- .probe = scsiback_probe,
- .remove = scsiback_remove,
- .otherend_changed = scsiback_frontend_changed
- };
- static void scsiback_init_pend(void *p)
- {
- struct vscsibk_pend *pend = p;
- int i;
- memset(pend, 0, sizeof(*pend));
- for (i = 0; i < VSCSI_MAX_GRANTS; i++)
- pend->grant_handles[i] = SCSIBACK_INVALID_HANDLE;
- }
- static int __init scsiback_init(void)
- {
- int ret;
- if (!xen_domain())
- return -ENODEV;
- pr_debug("xen-pvscsi: fabric module %s on %s/%s on "UTS_RELEASE"\n",
- VSCSI_VERSION, utsname()->sysname, utsname()->machine);
- scsiback_cachep = kmem_cache_create("vscsiif_cache",
- sizeof(struct vscsibk_pend), 0, 0, scsiback_init_pend);
- if (!scsiback_cachep)
- return -ENOMEM;
- ret = xenbus_register_backend(&scsiback_driver);
- if (ret)
- goto out_cache_destroy;
- ret = target_register_template(&scsiback_ops);
- if (ret)
- goto out_unregister_xenbus;
- return 0;
- out_unregister_xenbus:
- xenbus_unregister_driver(&scsiback_driver);
- out_cache_destroy:
- kmem_cache_destroy(scsiback_cachep);
- pr_err("%s: error %d\n", __func__, ret);
- return ret;
- }
- static void __exit scsiback_exit(void)
- {
- struct page *page;
- while (free_pages_num) {
- if (get_free_page(&page))
- BUG();
- gnttab_free_pages(1, &page);
- }
- target_unregister_template(&scsiback_ops);
- xenbus_unregister_driver(&scsiback_driver);
- kmem_cache_destroy(scsiback_cachep);
- }
- module_init(scsiback_init);
- module_exit(scsiback_exit);
- MODULE_DESCRIPTION("Xen SCSI backend driver");
- MODULE_LICENSE("Dual BSD/GPL");
- MODULE_ALIAS("xen-backend:vscsi");
- MODULE_AUTHOR("Juergen Gross <jgross@suse.com>");
|