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