linux/drivers/target/tcm_fc/tfc_sess.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2010 Cisco Systems, Inc.
   3 *
   4 * This program is free software; you can redistribute it and/or modify it
   5 * under the terms and conditions of the GNU General Public License,
   6 * version 2, as published by the Free Software Foundation.
   7 *
   8 * This program is distributed in the hope it will be useful, but WITHOUT
   9 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  10 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  11 * more details.
  12 *
  13 * You should have received a copy of the GNU General Public License along with
  14 * this program; if not, write to the Free Software Foundation, Inc.,
  15 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  16 */
  17
  18/* XXX TBD some includes may be extraneous */
  19
  20#include <linux/module.h>
  21#include <linux/moduleparam.h>
  22#include <generated/utsrelease.h>
  23#include <linux/utsname.h>
  24#include <linux/init.h>
  25#include <linux/slab.h>
  26#include <linux/kthread.h>
  27#include <linux/types.h>
  28#include <linux/string.h>
  29#include <linux/configfs.h>
  30#include <linux/ctype.h>
  31#include <linux/hash.h>
  32#include <linux/rcupdate.h>
  33#include <linux/rculist.h>
  34#include <linux/kref.h>
  35#include <asm/unaligned.h>
  36#include <scsi/scsi.h>
  37#include <scsi/scsi_host.h>
  38#include <scsi/scsi_device.h>
  39#include <scsi/scsi_cmnd.h>
  40#include <scsi/libfc.h>
  41
  42#include <target/target_core_base.h>
  43#include <target/target_core_fabric.h>
  44#include <target/target_core_configfs.h>
  45#include <target/configfs_macros.h>
  46
  47#include "tcm_fc.h"
  48
  49static void ft_sess_delete_all(struct ft_tport *);
  50
  51/*
  52 * Lookup or allocate target local port.
  53 * Caller holds ft_lport_lock.
  54 */
  55static struct ft_tport *ft_tport_create(struct fc_lport *lport)
  56{
  57        struct ft_tpg *tpg;
  58        struct ft_tport *tport;
  59        int i;
  60
  61        tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
  62        if (tport && tport->tpg)
  63                return tport;
  64
  65        tpg = ft_lport_find_tpg(lport);
  66        if (!tpg)
  67                return NULL;
  68
  69        if (tport) {
  70                tport->tpg = tpg;
  71                return tport;
  72        }
  73
  74        tport = kzalloc(sizeof(*tport), GFP_KERNEL);
  75        if (!tport)
  76                return NULL;
  77
  78        tport->lport = lport;
  79        tport->tpg = tpg;
  80        tpg->tport = tport;
  81        for (i = 0; i < FT_SESS_HASH_SIZE; i++)
  82                INIT_HLIST_HEAD(&tport->hash[i]);
  83
  84        rcu_assign_pointer(lport->prov[FC_TYPE_FCP], tport);
  85        return tport;
  86}
  87
  88/*
  89 * Free tport via RCU.
  90 */
  91static void ft_tport_rcu_free(struct rcu_head *rcu)
  92{
  93        struct ft_tport *tport = container_of(rcu, struct ft_tport, rcu);
  94
  95        kfree(tport);
  96}
  97
  98/*
  99 * Delete a target local port.
 100 * Caller holds ft_lport_lock.
 101 */
 102static void ft_tport_delete(struct ft_tport *tport)
 103{
 104        struct fc_lport *lport;
 105        struct ft_tpg *tpg;
 106
 107        ft_sess_delete_all(tport);
 108        lport = tport->lport;
 109        BUG_ON(tport != lport->prov[FC_TYPE_FCP]);
 110        rcu_assign_pointer(lport->prov[FC_TYPE_FCP], NULL);
 111
 112        tpg = tport->tpg;
 113        if (tpg) {
 114                tpg->tport = NULL;
 115                tport->tpg = NULL;
 116        }
 117        call_rcu(&tport->rcu, ft_tport_rcu_free);
 118}
 119
 120/*
 121 * Add local port.
 122 * Called thru fc_lport_iterate().
 123 */
 124void ft_lport_add(struct fc_lport *lport, void *arg)
 125{
 126        mutex_lock(&ft_lport_lock);
 127        ft_tport_create(lport);
 128        mutex_unlock(&ft_lport_lock);
 129}
 130
 131/*
 132 * Delete local port.
 133 * Called thru fc_lport_iterate().
 134 */
 135void ft_lport_del(struct fc_lport *lport, void *arg)
 136{
 137        struct ft_tport *tport;
 138
 139        mutex_lock(&ft_lport_lock);
 140        tport = lport->prov[FC_TYPE_FCP];
 141        if (tport)
 142                ft_tport_delete(tport);
 143        mutex_unlock(&ft_lport_lock);
 144}
 145
 146/*
 147 * Notification of local port change from libfc.
 148 * Create or delete local port and associated tport.
 149 */
 150int ft_lport_notify(struct notifier_block *nb, unsigned long event, void *arg)
 151{
 152        struct fc_lport *lport = arg;
 153
 154        switch (event) {
 155        case FC_LPORT_EV_ADD:
 156                ft_lport_add(lport, NULL);
 157                break;
 158        case FC_LPORT_EV_DEL:
 159                ft_lport_del(lport, NULL);
 160                break;
 161        }
 162        return NOTIFY_DONE;
 163}
 164
 165/*
 166 * Hash function for FC_IDs.
 167 */
 168static u32 ft_sess_hash(u32 port_id)
 169{
 170        return hash_32(port_id, FT_SESS_HASH_BITS);
 171}
 172
 173/*
 174 * Find session in local port.
 175 * Sessions and hash lists are RCU-protected.
 176 * A reference is taken which must be eventually freed.
 177 */
 178static struct ft_sess *ft_sess_get(struct fc_lport *lport, u32 port_id)
 179{
 180        struct ft_tport *tport;
 181        struct hlist_head *head;
 182        struct hlist_node *pos;
 183        struct ft_sess *sess;
 184
 185        rcu_read_lock();
 186        tport = rcu_dereference(lport->prov[FC_TYPE_FCP]);
 187        if (!tport)
 188                goto out;
 189
 190        head = &tport->hash[ft_sess_hash(port_id)];
 191        hlist_for_each_entry_rcu(sess, pos, head, hash) {
 192                if (sess->port_id == port_id) {
 193                        kref_get(&sess->kref);
 194                        rcu_read_unlock();
 195                        pr_debug("port_id %x found %p\n", port_id, sess);
 196                        return sess;
 197                }
 198        }
 199out:
 200        rcu_read_unlock();
 201        pr_debug("port_id %x not found\n", port_id);
 202        return NULL;
 203}
 204
 205/*
 206 * Allocate session and enter it in the hash for the local port.
 207 * Caller holds ft_lport_lock.
 208 */
 209static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,
 210                                      struct ft_node_acl *acl)
 211{
 212        struct ft_sess *sess;
 213        struct hlist_head *head;
 214        struct hlist_node *pos;
 215
 216        head = &tport->hash[ft_sess_hash(port_id)];
 217        hlist_for_each_entry_rcu(sess, pos, head, hash)
 218                if (sess->port_id == port_id)
 219                        return sess;
 220
 221        sess = kzalloc(sizeof(*sess), GFP_KERNEL);
 222        if (!sess)
 223                return NULL;
 224
 225        sess->se_sess = transport_init_session();
 226        if (IS_ERR(sess->se_sess)) {
 227                kfree(sess);
 228                return NULL;
 229        }
 230        sess->se_sess->se_node_acl = &acl->se_node_acl;
 231        sess->tport = tport;
 232        sess->port_id = port_id;
 233        kref_init(&sess->kref); /* ref for table entry */
 234        hlist_add_head_rcu(&sess->hash, head);
 235        tport->sess_count++;
 236
 237        pr_debug("port_id %x sess %p\n", port_id, sess);
 238
 239        transport_register_session(&tport->tpg->se_tpg, &acl->se_node_acl,
 240                                   sess->se_sess, sess);
 241        return sess;
 242}
 243
 244/*
 245 * Unhash the session.
 246 * Caller holds ft_lport_lock.
 247 */
 248static void ft_sess_unhash(struct ft_sess *sess)
 249{
 250        struct ft_tport *tport = sess->tport;
 251
 252        hlist_del_rcu(&sess->hash);
 253        BUG_ON(!tport->sess_count);
 254        tport->sess_count--;
 255        sess->port_id = -1;
 256        sess->params = 0;
 257}
 258
 259/*
 260 * Delete session from hash.
 261 * Caller holds ft_lport_lock.
 262 */
 263static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
 264{
 265        struct hlist_head *head;
 266        struct hlist_node *pos;
 267        struct ft_sess *sess;
 268
 269        head = &tport->hash[ft_sess_hash(port_id)];
 270        hlist_for_each_entry_rcu(sess, pos, head, hash) {
 271                if (sess->port_id == port_id) {
 272                        ft_sess_unhash(sess);
 273                        return sess;
 274                }
 275        }
 276        return NULL;
 277}
 278
 279/*
 280 * Delete all sessions from tport.
 281 * Caller holds ft_lport_lock.
 282 */
 283static void ft_sess_delete_all(struct ft_tport *tport)
 284{
 285        struct hlist_head *head;
 286        struct hlist_node *pos;
 287        struct ft_sess *sess;
 288
 289        for (head = tport->hash;
 290             head < &tport->hash[FT_SESS_HASH_SIZE]; head++) {
 291                hlist_for_each_entry_rcu(sess, pos, head, hash) {
 292                        ft_sess_unhash(sess);
 293                        transport_deregister_session_configfs(sess->se_sess);
 294                        ft_sess_put(sess);      /* release from table */
 295                }
 296        }
 297}
 298
 299/*
 300 * TCM ops for sessions.
 301 */
 302
 303/*
 304 * Determine whether session is allowed to be shutdown in the current context.
 305 * Returns non-zero if the session should be shutdown.
 306 */
 307int ft_sess_shutdown(struct se_session *se_sess)
 308{
 309        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 310
 311        pr_debug("port_id %x\n", sess->port_id);
 312        return 1;
 313}
 314
 315/*
 316 * Remove session and send PRLO.
 317 * This is called when the ACL is being deleted or queue depth is changing.
 318 */
 319void ft_sess_close(struct se_session *se_sess)
 320{
 321        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 322        struct fc_lport *lport;
 323        u32 port_id;
 324
 325        mutex_lock(&ft_lport_lock);
 326        lport = sess->tport->lport;
 327        port_id = sess->port_id;
 328        if (port_id == -1) {
 329                mutex_unlock(&ft_lport_lock);
 330                return;
 331        }
 332        pr_debug("port_id %x\n", port_id);
 333        ft_sess_unhash(sess);
 334        mutex_unlock(&ft_lport_lock);
 335        transport_deregister_session_configfs(se_sess);
 336        ft_sess_put(sess);
 337        /* XXX Send LOGO or PRLO */
 338        synchronize_rcu();              /* let transport deregister happen */
 339}
 340
 341void ft_sess_stop(struct se_session *se_sess, int sess_sleep, int conn_sleep)
 342{
 343        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 344
 345        pr_debug("port_id %x\n", sess->port_id);
 346}
 347
 348int ft_sess_logged_in(struct se_session *se_sess)
 349{
 350        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 351
 352        return sess->port_id != -1;
 353}
 354
 355u32 ft_sess_get_index(struct se_session *se_sess)
 356{
 357        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 358
 359        return sess->port_id;   /* XXX TBD probably not what is needed */
 360}
 361
 362u32 ft_sess_get_port_name(struct se_session *se_sess,
 363                          unsigned char *buf, u32 len)
 364{
 365        struct ft_sess *sess = se_sess->fabric_sess_ptr;
 366
 367        return ft_format_wwn(buf, len, sess->port_name);
 368}
 369
 370void ft_sess_set_erl0(struct se_session *se_sess)
 371{
 372        /* XXX TBD called when out of memory */
 373}
 374
 375/*
 376 * libfc ops involving sessions.
 377 */
 378
 379static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,
 380                          const struct fc_els_spp *rspp, struct fc_els_spp *spp)
 381{
 382        struct ft_tport *tport;
 383        struct ft_sess *sess;
 384        struct ft_node_acl *acl;
 385        u32 fcp_parm;
 386
 387        tport = ft_tport_create(rdata->local_port);
 388        if (!tport)
 389                return 0;       /* not a target for this local port */
 390
 391        acl = ft_acl_get(tport->tpg, rdata);
 392        if (!acl)
 393                return 0;
 394
 395        if (!rspp)
 396                goto fill;
 397
 398        if (rspp->spp_flags & (FC_SPP_OPA_VAL | FC_SPP_RPA_VAL))
 399                return FC_SPP_RESP_NO_PA;
 400
 401        /*
 402         * If both target and initiator bits are off, the SPP is invalid.
 403         */
 404        fcp_parm = ntohl(rspp->spp_params);
 405        if (!(fcp_parm & (FCP_SPPF_INIT_FCN | FCP_SPPF_TARG_FCN)))
 406                return FC_SPP_RESP_INVL;
 407
 408        /*
 409         * Create session (image pair) only if requested by
 410         * EST_IMG_PAIR flag and if the requestor is an initiator.
 411         */
 412        if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) {
 413                spp->spp_flags |= FC_SPP_EST_IMG_PAIR;
 414                if (!(fcp_parm & FCP_SPPF_INIT_FCN))
 415                        return FC_SPP_RESP_CONF;
 416                sess = ft_sess_create(tport, rdata->ids.port_id, acl);
 417                if (!sess)
 418                        return FC_SPP_RESP_RES;
 419                if (!sess->params)
 420                        rdata->prli_count++;
 421                sess->params = fcp_parm;
 422                sess->port_name = rdata->ids.port_name;
 423                sess->max_frame = rdata->maxframe_size;
 424
 425                /* XXX TBD - clearing actions.  unit attn, see 4.10 */
 426        }
 427
 428        /*
 429         * OR in our service parameters with other provider (initiator), if any.
 430         * TBD XXX - indicate RETRY capability?
 431         */
 432fill:
 433        fcp_parm = ntohl(spp->spp_params);
 434        spp->spp_params = htonl(fcp_parm | FCP_SPPF_TARG_FCN);
 435        return FC_SPP_RESP_ACK;
 436}
 437
 438/**
 439 * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
 440 * @rdata: remote port private
 441 * @spp_len: service parameter page length
 442 * @rspp: received service parameter page (NULL for outgoing PRLI)
 443 * @spp: response service parameter page
 444 *
 445 * Returns spp response code.
 446 */
 447static int ft_prli(struct fc_rport_priv *rdata, u32 spp_len,
 448                   const struct fc_els_spp *rspp, struct fc_els_spp *spp)
 449{
 450        int ret;
 451
 452        mutex_lock(&ft_lport_lock);
 453        ret = ft_prli_locked(rdata, spp_len, rspp, spp);
 454        mutex_unlock(&ft_lport_lock);
 455        pr_debug("port_id %x flags %x ret %x\n",
 456               rdata->ids.port_id, rspp ? rspp->spp_flags : 0, ret);
 457        return ret;
 458}
 459
 460static void ft_sess_rcu_free(struct rcu_head *rcu)
 461{
 462        struct ft_sess *sess = container_of(rcu, struct ft_sess, rcu);
 463
 464        transport_deregister_session(sess->se_sess);
 465        kfree(sess);
 466}
 467
 468static void ft_sess_free(struct kref *kref)
 469{
 470        struct ft_sess *sess = container_of(kref, struct ft_sess, kref);
 471
 472        call_rcu(&sess->rcu, ft_sess_rcu_free);
 473}
 474
 475void ft_sess_put(struct ft_sess *sess)
 476{
 477        int sess_held = atomic_read(&sess->kref.refcount);
 478
 479        BUG_ON(!sess_held);
 480        kref_put(&sess->kref, ft_sess_free);
 481}
 482
 483static void ft_prlo(struct fc_rport_priv *rdata)
 484{
 485        struct ft_sess *sess;
 486        struct ft_tport *tport;
 487
 488        mutex_lock(&ft_lport_lock);
 489        tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]);
 490        if (!tport) {
 491                mutex_unlock(&ft_lport_lock);
 492                return;
 493        }
 494        sess = ft_sess_delete(tport, rdata->ids.port_id);
 495        if (!sess) {
 496                mutex_unlock(&ft_lport_lock);
 497                return;
 498        }
 499        mutex_unlock(&ft_lport_lock);
 500        transport_deregister_session_configfs(sess->se_sess);
 501        ft_sess_put(sess);              /* release from table */
 502        rdata->prli_count--;
 503        /* XXX TBD - clearing actions.  unit attn, see 4.10 */
 504}
 505
 506/*
 507 * Handle incoming FCP request.
 508 * Caller has verified that the frame is type FCP.
 509 */
 510static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
 511{
 512        struct ft_sess *sess;
 513        u32 sid = fc_frame_sid(fp);
 514
 515        pr_debug("sid %x\n", sid);
 516
 517        sess = ft_sess_get(lport, sid);
 518        if (!sess) {
 519                pr_debug("sid %x sess lookup failed\n", sid);
 520                /* TBD XXX - if FCP_CMND, send PRLO */
 521                fc_frame_free(fp);
 522                return;
 523        }
 524        ft_recv_req(sess, fp);  /* must do ft_sess_put() */
 525}
 526
 527/*
 528 * Provider ops for libfc.
 529 */
 530struct fc4_prov ft_prov = {
 531        .prli = ft_prli,
 532        .prlo = ft_prlo,
 533        .recv = ft_recv,
 534        .module = THIS_MODULE,
 535};
 536