| /* | 
 |  *  IBM eServer eHCA Infiniband device driver for Linux on POWER | 
 |  * | 
 |  *  address vector functions | 
 |  * | 
 |  *  Authors: Hoang-Nam Nguyen <hnguyen@de.ibm.com> | 
 |  *           Khadija Souissi <souissik@de.ibm.com> | 
 |  *           Reinhard Ernst <rernst@de.ibm.com> | 
 |  *           Christoph Raisch <raisch@de.ibm.com> | 
 |  * | 
 |  *  Copyright (c) 2005 IBM Corporation | 
 |  * | 
 |  *  All rights reserved. | 
 |  * | 
 |  *  This source code is distributed under a dual license of GPL v2.0 and OpenIB | 
 |  *  BSD. | 
 |  * | 
 |  * OpenIB BSD License | 
 |  * | 
 |  * Redistribution and use in source and binary forms, with or without | 
 |  * modification, are permitted provided that the following conditions are met: | 
 |  * | 
 |  * Redistributions of source code must retain the above copyright notice, this | 
 |  * list of conditions and the following disclaimer. | 
 |  * | 
 |  * Redistributions in binary form must reproduce the above copyright notice, | 
 |  * this list of conditions and the following disclaimer in the documentation | 
 |  * and/or other materials | 
 |  * provided with the distribution. | 
 |  * | 
 |  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | 
 |  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | 
 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | 
 |  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | 
 |  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | 
 |  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | 
 |  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR | 
 |  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER | 
 |  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | 
 |  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | 
 |  * POSSIBILITY OF SUCH DAMAGE. | 
 |  */ | 
 |  | 
 | #include "ehca_tools.h" | 
 | #include "ehca_iverbs.h" | 
 | #include "hcp_if.h" | 
 |  | 
 | static struct kmem_cache *av_cache; | 
 |  | 
 | int ehca_calc_ipd(struct ehca_shca *shca, int port, | 
 | 		  enum ib_rate path_rate, u32 *ipd) | 
 | { | 
 | 	int path = ib_rate_to_mult(path_rate); | 
 | 	int link, ret; | 
 | 	struct ib_port_attr pa; | 
 |  | 
 | 	if (path_rate == IB_RATE_PORT_CURRENT) { | 
 | 		*ipd = 0; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	if (unlikely(path < 0)) { | 
 | 		ehca_err(&shca->ib_device, "Invalid static rate! path_rate=%x", | 
 | 			 path_rate); | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	ret = ehca_query_port(&shca->ib_device, port, &pa); | 
 | 	if (unlikely(ret < 0)) { | 
 | 		ehca_err(&shca->ib_device, "Failed to query port  ret=%i", ret); | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	link = ib_width_enum_to_int(pa.active_width) * pa.active_speed; | 
 |  | 
 | 	if (path >= link) | 
 | 		/* no need to throttle if path faster than link */ | 
 | 		*ipd = 0; | 
 | 	else | 
 | 		/* IPD = round((link / path) - 1) */ | 
 | 		*ipd = ((link + (path >> 1)) / path) - 1; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | struct ib_ah *ehca_create_ah(struct ib_pd *pd, struct ib_ah_attr *ah_attr) | 
 | { | 
 | 	int ret; | 
 | 	struct ehca_av *av; | 
 | 	struct ehca_shca *shca = container_of(pd->device, struct ehca_shca, | 
 | 					      ib_device); | 
 |  | 
 | 	av = kmem_cache_alloc(av_cache, GFP_KERNEL); | 
 | 	if (!av) { | 
 | 		ehca_err(pd->device, "Out of memory pd=%p ah_attr=%p", | 
 | 			 pd, ah_attr); | 
 | 		return ERR_PTR(-ENOMEM); | 
 | 	} | 
 |  | 
 | 	av->av.sl = ah_attr->sl; | 
 | 	av->av.dlid = ah_attr->dlid; | 
 | 	av->av.slid_path_bits = ah_attr->src_path_bits; | 
 |  | 
 | 	if (ehca_static_rate < 0) { | 
 | 		u32 ipd; | 
 | 		if (ehca_calc_ipd(shca, ah_attr->port_num, | 
 | 				  ah_attr->static_rate, &ipd)) { | 
 | 			ret = -EINVAL; | 
 | 			goto create_ah_exit1; | 
 | 		} | 
 | 		av->av.ipd = ipd; | 
 | 	} else | 
 | 		av->av.ipd = ehca_static_rate; | 
 |  | 
 | 	av->av.lnh = ah_attr->ah_flags; | 
 | 	av->av.grh.word_0 = EHCA_BMASK_SET(GRH_IPVERSION_MASK, 6); | 
 | 	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_TCLASS_MASK, | 
 | 					    ah_attr->grh.traffic_class); | 
 | 	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK, | 
 | 					    ah_attr->grh.flow_label); | 
 | 	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK, | 
 | 					    ah_attr->grh.hop_limit); | 
 | 	av->av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1B); | 
 | 	/* set sgid in grh.word_1 */ | 
 | 	if (ah_attr->ah_flags & IB_AH_GRH) { | 
 | 		int rc; | 
 | 		struct ib_port_attr port_attr; | 
 | 		union ib_gid gid; | 
 | 		memset(&port_attr, 0, sizeof(port_attr)); | 
 | 		rc = ehca_query_port(pd->device, ah_attr->port_num, | 
 | 				     &port_attr); | 
 | 		if (rc) { /* invalid port number */ | 
 | 			ret = -EINVAL; | 
 | 			ehca_err(pd->device, "Invalid port number " | 
 | 				 "ehca_query_port() returned %x " | 
 | 				 "pd=%p ah_attr=%p", rc, pd, ah_attr); | 
 | 			goto create_ah_exit1; | 
 | 		} | 
 | 		memset(&gid, 0, sizeof(gid)); | 
 | 		rc = ehca_query_gid(pd->device, | 
 | 				    ah_attr->port_num, | 
 | 				    ah_attr->grh.sgid_index, &gid); | 
 | 		if (rc) { | 
 | 			ret = -EINVAL; | 
 | 			ehca_err(pd->device, "Failed to retrieve sgid " | 
 | 				 "ehca_query_gid() returned %x " | 
 | 				 "pd=%p ah_attr=%p", rc, pd, ah_attr); | 
 | 			goto create_ah_exit1; | 
 | 		} | 
 | 		memcpy(&av->av.grh.word_1, &gid, sizeof(gid)); | 
 | 	} | 
 | 	av->av.pmtu = shca->max_mtu; | 
 |  | 
 | 	/* dgid comes in grh.word_3 */ | 
 | 	memcpy(&av->av.grh.word_3, &ah_attr->grh.dgid, | 
 | 	       sizeof(ah_attr->grh.dgid)); | 
 |  | 
 | 	return &av->ib_ah; | 
 |  | 
 | create_ah_exit1: | 
 | 	kmem_cache_free(av_cache, av); | 
 |  | 
 | 	return ERR_PTR(ret); | 
 | } | 
 |  | 
 | int ehca_modify_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr) | 
 | { | 
 | 	struct ehca_av *av; | 
 | 	struct ehca_ud_av new_ehca_av; | 
 | 	struct ehca_shca *shca = container_of(ah->pd->device, struct ehca_shca, | 
 | 					      ib_device); | 
 |  | 
 | 	memset(&new_ehca_av, 0, sizeof(new_ehca_av)); | 
 | 	new_ehca_av.sl = ah_attr->sl; | 
 | 	new_ehca_av.dlid = ah_attr->dlid; | 
 | 	new_ehca_av.slid_path_bits = ah_attr->src_path_bits; | 
 | 	new_ehca_av.ipd = ah_attr->static_rate; | 
 | 	new_ehca_av.lnh = EHCA_BMASK_SET(GRH_FLAG_MASK, | 
 | 					 (ah_attr->ah_flags & IB_AH_GRH) > 0); | 
 | 	new_ehca_av.grh.word_0 = EHCA_BMASK_SET(GRH_TCLASS_MASK, | 
 | 						ah_attr->grh.traffic_class); | 
 | 	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_FLOWLABEL_MASK, | 
 | 						 ah_attr->grh.flow_label); | 
 | 	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_HOPLIMIT_MASK, | 
 | 						 ah_attr->grh.hop_limit); | 
 | 	new_ehca_av.grh.word_0 |= EHCA_BMASK_SET(GRH_NEXTHEADER_MASK, 0x1b); | 
 |  | 
 | 	/* set sgid in grh.word_1 */ | 
 | 	if (ah_attr->ah_flags & IB_AH_GRH) { | 
 | 		int rc; | 
 | 		struct ib_port_attr port_attr; | 
 | 		union ib_gid gid; | 
 | 		memset(&port_attr, 0, sizeof(port_attr)); | 
 | 		rc = ehca_query_port(ah->device, ah_attr->port_num, | 
 | 				     &port_attr); | 
 | 		if (rc) { /* invalid port number */ | 
 | 			ehca_err(ah->device, "Invalid port number " | 
 | 				 "ehca_query_port() returned %x " | 
 | 				 "ah=%p ah_attr=%p port_num=%x", | 
 | 				 rc, ah, ah_attr, ah_attr->port_num); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		memset(&gid, 0, sizeof(gid)); | 
 | 		rc = ehca_query_gid(ah->device, | 
 | 				    ah_attr->port_num, | 
 | 				    ah_attr->grh.sgid_index, &gid); | 
 | 		if (rc) { | 
 | 			ehca_err(ah->device, "Failed to retrieve sgid " | 
 | 				 "ehca_query_gid() returned %x " | 
 | 				 "ah=%p ah_attr=%p port_num=%x " | 
 | 				 "sgid_index=%x", | 
 | 				 rc, ah, ah_attr, ah_attr->port_num, | 
 | 				 ah_attr->grh.sgid_index); | 
 | 			return -EINVAL; | 
 | 		} | 
 | 		memcpy(&new_ehca_av.grh.word_1, &gid, sizeof(gid)); | 
 | 	} | 
 |  | 
 | 	new_ehca_av.pmtu = shca->max_mtu; | 
 |  | 
 | 	memcpy(&new_ehca_av.grh.word_3, &ah_attr->grh.dgid, | 
 | 	       sizeof(ah_attr->grh.dgid)); | 
 |  | 
 | 	av = container_of(ah, struct ehca_av, ib_ah); | 
 | 	av->av = new_ehca_av; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int ehca_query_ah(struct ib_ah *ah, struct ib_ah_attr *ah_attr) | 
 | { | 
 | 	struct ehca_av *av = container_of(ah, struct ehca_av, ib_ah); | 
 |  | 
 | 	memcpy(&ah_attr->grh.dgid, &av->av.grh.word_3, | 
 | 	       sizeof(ah_attr->grh.dgid)); | 
 | 	ah_attr->sl = av->av.sl; | 
 |  | 
 | 	ah_attr->dlid = av->av.dlid; | 
 |  | 
 | 	ah_attr->src_path_bits = av->av.slid_path_bits; | 
 | 	ah_attr->static_rate = av->av.ipd; | 
 | 	ah_attr->ah_flags = EHCA_BMASK_GET(GRH_FLAG_MASK, av->av.lnh); | 
 | 	ah_attr->grh.traffic_class = EHCA_BMASK_GET(GRH_TCLASS_MASK, | 
 | 						    av->av.grh.word_0); | 
 | 	ah_attr->grh.hop_limit = EHCA_BMASK_GET(GRH_HOPLIMIT_MASK, | 
 | 						av->av.grh.word_0); | 
 | 	ah_attr->grh.flow_label = EHCA_BMASK_GET(GRH_FLOWLABEL_MASK, | 
 | 						 av->av.grh.word_0); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int ehca_destroy_ah(struct ib_ah *ah) | 
 | { | 
 | 	kmem_cache_free(av_cache, container_of(ah, struct ehca_av, ib_ah)); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | int ehca_init_av_cache(void) | 
 | { | 
 | 	av_cache = kmem_cache_create("ehca_cache_av", | 
 | 				   sizeof(struct ehca_av), 0, | 
 | 				   SLAB_HWCACHE_ALIGN, | 
 | 				   NULL); | 
 | 	if (!av_cache) | 
 | 		return -ENOMEM; | 
 | 	return 0; | 
 | } | 
 |  | 
 | void ehca_cleanup_av_cache(void) | 
 | { | 
 | 	if (av_cache) | 
 | 		kmem_cache_destroy(av_cache); | 
 | } |