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