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