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