brintos

brintos / linux-shallow public Read only

0
0
Text · 7.5 KiB · 562df2b Raw
298 lines · c
1/*2 * Copyright (c) 2004 Topspin Communications.  All rights reserved.3 *4 * This software is available to you under a choice of one of two5 * licenses.  You may choose to be licensed under the terms of the GNU6 * General Public License (GPL) Version 2, available from the file7 * COPYING in the main directory of this source tree, or the8 * OpenIB.org BSD license below:9 *10 *     Redistribution and use in source and binary forms, with or11 *     without modification, are permitted provided that the following12 *     conditions are met:13 *14 *      - Redistributions of source code must retain the above15 *        copyright notice, this list of conditions and the following16 *        disclaimer.17 *18 *      - Redistributions in binary form must reproduce the above19 *        copyright notice, this list of conditions and the following20 *        disclaimer in the documentation and/or other materials21 *        provided with the distribution.22 *23 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,24 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF25 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND26 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS27 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN28 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN29 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE30 * SOFTWARE.31 */32 33#include <linux/sched/signal.h>34 35#include <linux/init.h>36#include <linux/seq_file.h>37 38#include <linux/uaccess.h>39 40#include "ipoib.h"41 42static ssize_t parent_show(struct device *d, struct device_attribute *attr,43			   char *buf)44{45	struct net_device *dev = to_net_dev(d);46	struct ipoib_dev_priv *priv = ipoib_priv(dev);47 48	return sysfs_emit(buf, "%s\n", priv->parent->name);49}50static DEVICE_ATTR_RO(parent);51 52static bool is_child_unique(struct ipoib_dev_priv *ppriv,53			    struct ipoib_dev_priv *priv)54{55	struct ipoib_dev_priv *tpriv;56 57	ASSERT_RTNL();58 59	/*60	 * Since the legacy sysfs interface uses pkey for deletion it cannot61	 * support more than one interface with the same pkey, it creates62	 * ambiguity.  The RTNL interface deletes using the netdev so it does63	 * not have a problem to support duplicated pkeys.64	 */65	if (priv->child_type != IPOIB_LEGACY_CHILD)66		return true;67 68	/*69	 * First ensure this isn't a duplicate. We check the parent device and70	 * then all of the legacy child interfaces to make sure the Pkey71	 * doesn't match.72	 */73	if (ppriv->pkey == priv->pkey)74		return false;75 76	list_for_each_entry(tpriv, &ppriv->child_intfs, list) {77		if (tpriv->pkey == priv->pkey &&78		    tpriv->child_type == IPOIB_LEGACY_CHILD)79			return false;80	}81 82	return true;83}84 85/*86 * NOTE: If this function fails then the priv->dev will remain valid, however87 * priv will have been freed and must not be touched by caller in the error88 * case.89 *90 * If (ndev->reg_state == NETREG_UNINITIALIZED) then it is up to the caller to91 * free the net_device (just as rtnl_newlink does) otherwise the net_device92 * will be freed when the rtnl is unlocked.93 */94int __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,95		     u16 pkey, int type)96{97	struct net_device *ndev = priv->dev;98	int result;99	struct rdma_netdev *rn = netdev_priv(ndev);100 101	ASSERT_RTNL();102 103	/*104	 * We do not need to touch priv if register_netdevice fails, so just105	 * always use this flow.106	 */107	ndev->priv_destructor = ipoib_intf_free;108 109	/*110	 * Racing with unregister of the parent must be prevented by the111	 * caller.112	 */113	WARN_ON(ppriv->dev->reg_state != NETREG_REGISTERED);114 115	if (pkey == 0 || pkey == 0x8000) {116		result = -EINVAL;117		goto out_early;118	}119 120	rn->mtu = priv->mcast_mtu;121 122	priv->parent = ppriv->dev;123	priv->pkey = pkey;124	priv->child_type = type;125 126	if (!is_child_unique(ppriv, priv)) {127		result = -ENOTUNIQ;128		goto out_early;129	}130 131	result = register_netdevice(ndev);132	if (result) {133		ipoib_warn(priv, "failed to initialize; error %i", result);134 135		/*136		 * register_netdevice sometimes calls priv_destructor,137		 * sometimes not. Make sure it was done.138		 */139		goto out_early;140	}141 142	/* RTNL childs don't need proprietary sysfs entries */143	if (type == IPOIB_LEGACY_CHILD) {144		if (ipoib_cm_add_mode_attr(ndev))145			goto sysfs_failed;146		if (ipoib_add_pkey_attr(ndev))147			goto sysfs_failed;148		if (ipoib_add_umcast_attr(ndev))149			goto sysfs_failed;150 151		if (device_create_file(&ndev->dev, &dev_attr_parent))152			goto sysfs_failed;153	}154 155	return 0;156 157sysfs_failed:158	unregister_netdevice(priv->dev);159	return -ENOMEM;160 161out_early:162	if (ndev->priv_destructor)163		ndev->priv_destructor(ndev);164	return result;165}166 167int ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)168{169	struct ipoib_dev_priv *ppriv, *priv;170	char intf_name[IFNAMSIZ];171	struct net_device *ndev;172	int result;173 174	if (!capable(CAP_NET_ADMIN))175		return -EPERM;176 177	if (!rtnl_trylock())178		return restart_syscall();179 180	if (pdev->reg_state != NETREG_REGISTERED) {181		rtnl_unlock();182		return -EPERM;183	}184 185	ppriv = ipoib_priv(pdev);186 187	/* If you increase IFNAMSIZ, update snprintf below188	 * to allow longer names.189	 */190	BUILD_BUG_ON(IFNAMSIZ != 16);191	snprintf(intf_name, sizeof(intf_name), "%.10s.%04x", ppriv->dev->name,192		 pkey);193 194	ndev = ipoib_intf_alloc(ppriv->ca, ppriv->port, intf_name);195	if (IS_ERR(ndev)) {196		result = PTR_ERR(ndev);197		goto out;198	}199	priv = ipoib_priv(ndev);200 201	ndev->rtnl_link_ops = ipoib_get_link_ops();202 203	result = __ipoib_vlan_add(ppriv, priv, pkey, IPOIB_LEGACY_CHILD);204 205	if (result && ndev->reg_state == NETREG_UNINITIALIZED)206		free_netdev(ndev);207 208out:209	rtnl_unlock();210 211	return result;212}213 214struct ipoib_vlan_delete_work {215	struct work_struct work;216	struct net_device *dev;217};218 219/*220 * sysfs callbacks of a netdevice cannot obtain the rtnl lock as221 * unregister_netdev ultimately deletes the sysfs files while holding the rtnl222 * lock. This deadlocks the system.223 *224 * A callback can use rtnl_trylock to avoid the deadlock but it cannot call225 * unregister_netdev as that internally takes and releases the rtnl_lock.  So226 * instead we find the netdev to unregister and then do the actual unregister227 * from the global work queue where we can obtain the rtnl_lock safely.228 */229static void ipoib_vlan_delete_task(struct work_struct *work)230{231	struct ipoib_vlan_delete_work *pwork =232		container_of(work, struct ipoib_vlan_delete_work, work);233	struct net_device *dev = pwork->dev;234 235	rtnl_lock();236 237	/* Unregistering tasks can race with another task or parent removal */238	if (dev->reg_state == NETREG_REGISTERED) {239		struct ipoib_dev_priv *priv = ipoib_priv(dev);240		struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);241 242		ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);243		unregister_netdevice(dev);244	}245 246	rtnl_unlock();247 248	kfree(pwork);249}250 251int ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)252{253	struct ipoib_dev_priv *ppriv, *priv, *tpriv;254	int rc;255 256	if (!capable(CAP_NET_ADMIN))257		return -EPERM;258 259	if (!rtnl_trylock())260		return restart_syscall();261 262	if (pdev->reg_state != NETREG_REGISTERED) {263		rtnl_unlock();264		return -EPERM;265	}266 267	ppriv = ipoib_priv(pdev);268 269	rc = -ENODEV;270	list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {271		if (priv->pkey == pkey &&272		    priv->child_type == IPOIB_LEGACY_CHILD) {273			struct ipoib_vlan_delete_work *work;274 275			work = kmalloc(sizeof(*work), GFP_KERNEL);276			if (!work) {277				rc = -ENOMEM;278				goto out;279			}280 281			down_write(&ppriv->vlan_rwsem);282			list_del_init(&priv->list);283			up_write(&ppriv->vlan_rwsem);284			work->dev = priv->dev;285			INIT_WORK(&work->work, ipoib_vlan_delete_task);286			queue_work(ipoib_workqueue, &work->work);287 288			rc = 0;289			break;290		}291	}292 293out:294	rtnl_unlock();295 296	return rc;297}298