18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004 Topspin Communications.  All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
58c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
98c2ecf20Sopenharmony_ci *
108c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
118c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
128c2ecf20Sopenharmony_ci *     conditions are met:
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *      - Redistributions of source code must retain the above
158c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
168c2ecf20Sopenharmony_ci *        disclaimer.
178c2ecf20Sopenharmony_ci *
188c2ecf20Sopenharmony_ci *      - Redistributions in binary form must reproduce the above
198c2ecf20Sopenharmony_ci *        copyright notice, this list of conditions and the following
208c2ecf20Sopenharmony_ci *        disclaimer in the documentation and/or other materials
218c2ecf20Sopenharmony_ci *        provided with the distribution.
228c2ecf20Sopenharmony_ci *
238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
308c2ecf20Sopenharmony_ci * SOFTWARE.
318c2ecf20Sopenharmony_ci */
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#include <linux/module.h>
348c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#include <linux/init.h>
378c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "ipoib.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic ssize_t show_parent(struct device *d, struct device_attribute *attr,
448c2ecf20Sopenharmony_ci			   char *buf)
458c2ecf20Sopenharmony_ci{
468c2ecf20Sopenharmony_ci	struct net_device *dev = to_net_dev(d);
478c2ecf20Sopenharmony_ci	struct ipoib_dev_priv *priv = ipoib_priv(dev);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	return sprintf(buf, "%s\n", priv->parent->name);
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_cistatic DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic bool is_child_unique(struct ipoib_dev_priv *ppriv,
548c2ecf20Sopenharmony_ci			    struct ipoib_dev_priv *priv)
558c2ecf20Sopenharmony_ci{
568c2ecf20Sopenharmony_ci	struct ipoib_dev_priv *tpriv;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	ASSERT_RTNL();
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	/*
618c2ecf20Sopenharmony_ci	 * Since the legacy sysfs interface uses pkey for deletion it cannot
628c2ecf20Sopenharmony_ci	 * support more than one interface with the same pkey, it creates
638c2ecf20Sopenharmony_ci	 * ambiguity.  The RTNL interface deletes using the netdev so it does
648c2ecf20Sopenharmony_ci	 * not have a problem to support duplicated pkeys.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	if (priv->child_type != IPOIB_LEGACY_CHILD)
678c2ecf20Sopenharmony_ci		return true;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	/*
708c2ecf20Sopenharmony_ci	 * First ensure this isn't a duplicate. We check the parent device and
718c2ecf20Sopenharmony_ci	 * then all of the legacy child interfaces to make sure the Pkey
728c2ecf20Sopenharmony_ci	 * doesn't match.
738c2ecf20Sopenharmony_ci	 */
748c2ecf20Sopenharmony_ci	if (ppriv->pkey == priv->pkey)
758c2ecf20Sopenharmony_ci		return false;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	list_for_each_entry(tpriv, &ppriv->child_intfs, list) {
788c2ecf20Sopenharmony_ci		if (tpriv->pkey == priv->pkey &&
798c2ecf20Sopenharmony_ci		    tpriv->child_type == IPOIB_LEGACY_CHILD)
808c2ecf20Sopenharmony_ci			return false;
818c2ecf20Sopenharmony_ci	}
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return true;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci/*
878c2ecf20Sopenharmony_ci * NOTE: If this function fails then the priv->dev will remain valid, however
888c2ecf20Sopenharmony_ci * priv will have been freed and must not be touched by caller in the error
898c2ecf20Sopenharmony_ci * case.
908c2ecf20Sopenharmony_ci *
918c2ecf20Sopenharmony_ci * If (ndev->reg_state == NETREG_UNINITIALIZED) then it is up to the caller to
928c2ecf20Sopenharmony_ci * free the net_device (just as rtnl_newlink does) otherwise the net_device
938c2ecf20Sopenharmony_ci * will be freed when the rtnl is unlocked.
948c2ecf20Sopenharmony_ci */
958c2ecf20Sopenharmony_ciint __ipoib_vlan_add(struct ipoib_dev_priv *ppriv, struct ipoib_dev_priv *priv,
968c2ecf20Sopenharmony_ci		     u16 pkey, int type)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct net_device *ndev = priv->dev;
998c2ecf20Sopenharmony_ci	int result;
1008c2ecf20Sopenharmony_ci	struct rdma_netdev *rn = netdev_priv(ndev);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	ASSERT_RTNL();
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	/*
1058c2ecf20Sopenharmony_ci	 * We do not need to touch priv if register_netdevice fails, so just
1068c2ecf20Sopenharmony_ci	 * always use this flow.
1078c2ecf20Sopenharmony_ci	 */
1088c2ecf20Sopenharmony_ci	ndev->priv_destructor = ipoib_intf_free;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	/*
1118c2ecf20Sopenharmony_ci	 * Racing with unregister of the parent must be prevented by the
1128c2ecf20Sopenharmony_ci	 * caller.
1138c2ecf20Sopenharmony_ci	 */
1148c2ecf20Sopenharmony_ci	WARN_ON(ppriv->dev->reg_state != NETREG_REGISTERED);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (pkey == 0 || pkey == 0x8000) {
1178c2ecf20Sopenharmony_ci		result = -EINVAL;
1188c2ecf20Sopenharmony_ci		goto out_early;
1198c2ecf20Sopenharmony_ci	}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	rn->mtu = priv->mcast_mtu;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	priv->parent = ppriv->dev;
1248c2ecf20Sopenharmony_ci	priv->pkey = pkey;
1258c2ecf20Sopenharmony_ci	priv->child_type = type;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	if (!is_child_unique(ppriv, priv)) {
1288c2ecf20Sopenharmony_ci		result = -ENOTUNIQ;
1298c2ecf20Sopenharmony_ci		goto out_early;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	result = register_netdevice(ndev);
1338c2ecf20Sopenharmony_ci	if (result) {
1348c2ecf20Sopenharmony_ci		ipoib_warn(priv, "failed to initialize; error %i", result);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci		/*
1378c2ecf20Sopenharmony_ci		 * register_netdevice sometimes calls priv_destructor,
1388c2ecf20Sopenharmony_ci		 * sometimes not. Make sure it was done.
1398c2ecf20Sopenharmony_ci		 */
1408c2ecf20Sopenharmony_ci		goto out_early;
1418c2ecf20Sopenharmony_ci	}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci	/* RTNL childs don't need proprietary sysfs entries */
1448c2ecf20Sopenharmony_ci	if (type == IPOIB_LEGACY_CHILD) {
1458c2ecf20Sopenharmony_ci		if (ipoib_cm_add_mode_attr(ndev))
1468c2ecf20Sopenharmony_ci			goto sysfs_failed;
1478c2ecf20Sopenharmony_ci		if (ipoib_add_pkey_attr(ndev))
1488c2ecf20Sopenharmony_ci			goto sysfs_failed;
1498c2ecf20Sopenharmony_ci		if (ipoib_add_umcast_attr(ndev))
1508c2ecf20Sopenharmony_ci			goto sysfs_failed;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci		if (device_create_file(&ndev->dev, &dev_attr_parent))
1538c2ecf20Sopenharmony_ci			goto sysfs_failed;
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_cisysfs_failed:
1598c2ecf20Sopenharmony_ci	unregister_netdevice(priv->dev);
1608c2ecf20Sopenharmony_ci	return -ENOMEM;
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ciout_early:
1638c2ecf20Sopenharmony_ci	if (ndev->priv_destructor)
1648c2ecf20Sopenharmony_ci		ndev->priv_destructor(ndev);
1658c2ecf20Sopenharmony_ci	return result;
1668c2ecf20Sopenharmony_ci}
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciint ipoib_vlan_add(struct net_device *pdev, unsigned short pkey)
1698c2ecf20Sopenharmony_ci{
1708c2ecf20Sopenharmony_ci	struct ipoib_dev_priv *ppriv, *priv;
1718c2ecf20Sopenharmony_ci	char intf_name[IFNAMSIZ];
1728c2ecf20Sopenharmony_ci	struct net_device *ndev;
1738c2ecf20Sopenharmony_ci	int result;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
1768c2ecf20Sopenharmony_ci		return -EPERM;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if (!rtnl_trylock())
1798c2ecf20Sopenharmony_ci		return restart_syscall();
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (pdev->reg_state != NETREG_REGISTERED) {
1828c2ecf20Sopenharmony_ci		rtnl_unlock();
1838c2ecf20Sopenharmony_ci		return -EPERM;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	ppriv = ipoib_priv(pdev);
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	snprintf(intf_name, sizeof(intf_name), "%s.%04x",
1898c2ecf20Sopenharmony_ci		 ppriv->dev->name, pkey);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	ndev = ipoib_intf_alloc(ppriv->ca, ppriv->port, intf_name);
1928c2ecf20Sopenharmony_ci	if (IS_ERR(ndev)) {
1938c2ecf20Sopenharmony_ci		result = PTR_ERR(ndev);
1948c2ecf20Sopenharmony_ci		goto out;
1958c2ecf20Sopenharmony_ci	}
1968c2ecf20Sopenharmony_ci	priv = ipoib_priv(ndev);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	ndev->rtnl_link_ops = ipoib_get_link_ops();
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	result = __ipoib_vlan_add(ppriv, priv, pkey, IPOIB_LEGACY_CHILD);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (result && ndev->reg_state == NETREG_UNINITIALIZED)
2038c2ecf20Sopenharmony_ci		free_netdev(ndev);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ciout:
2068c2ecf20Sopenharmony_ci	rtnl_unlock();
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	return result;
2098c2ecf20Sopenharmony_ci}
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cistruct ipoib_vlan_delete_work {
2128c2ecf20Sopenharmony_ci	struct work_struct work;
2138c2ecf20Sopenharmony_ci	struct net_device *dev;
2148c2ecf20Sopenharmony_ci};
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/*
2178c2ecf20Sopenharmony_ci * sysfs callbacks of a netdevice cannot obtain the rtnl lock as
2188c2ecf20Sopenharmony_ci * unregister_netdev ultimately deletes the sysfs files while holding the rtnl
2198c2ecf20Sopenharmony_ci * lock. This deadlocks the system.
2208c2ecf20Sopenharmony_ci *
2218c2ecf20Sopenharmony_ci * A callback can use rtnl_trylock to avoid the deadlock but it cannot call
2228c2ecf20Sopenharmony_ci * unregister_netdev as that internally takes and releases the rtnl_lock.  So
2238c2ecf20Sopenharmony_ci * instead we find the netdev to unregister and then do the actual unregister
2248c2ecf20Sopenharmony_ci * from the global work queue where we can obtain the rtnl_lock safely.
2258c2ecf20Sopenharmony_ci */
2268c2ecf20Sopenharmony_cistatic void ipoib_vlan_delete_task(struct work_struct *work)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct ipoib_vlan_delete_work *pwork =
2298c2ecf20Sopenharmony_ci		container_of(work, struct ipoib_vlan_delete_work, work);
2308c2ecf20Sopenharmony_ci	struct net_device *dev = pwork->dev;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	rtnl_lock();
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	/* Unregistering tasks can race with another task or parent removal */
2358c2ecf20Sopenharmony_ci	if (dev->reg_state == NETREG_REGISTERED) {
2368c2ecf20Sopenharmony_ci		struct ipoib_dev_priv *priv = ipoib_priv(dev);
2378c2ecf20Sopenharmony_ci		struct ipoib_dev_priv *ppriv = ipoib_priv(priv->parent);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci		ipoib_dbg(ppriv, "delete child vlan %s\n", dev->name);
2408c2ecf20Sopenharmony_ci		unregister_netdevice(dev);
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	rtnl_unlock();
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci	kfree(pwork);
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ciint ipoib_vlan_delete(struct net_device *pdev, unsigned short pkey)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	struct ipoib_dev_priv *ppriv, *priv, *tpriv;
2518c2ecf20Sopenharmony_ci	int rc;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (!capable(CAP_NET_ADMIN))
2548c2ecf20Sopenharmony_ci		return -EPERM;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (!rtnl_trylock())
2578c2ecf20Sopenharmony_ci		return restart_syscall();
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	if (pdev->reg_state != NETREG_REGISTERED) {
2608c2ecf20Sopenharmony_ci		rtnl_unlock();
2618c2ecf20Sopenharmony_ci		return -EPERM;
2628c2ecf20Sopenharmony_ci	}
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	ppriv = ipoib_priv(pdev);
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	rc = -ENODEV;
2678c2ecf20Sopenharmony_ci	list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
2688c2ecf20Sopenharmony_ci		if (priv->pkey == pkey &&
2698c2ecf20Sopenharmony_ci		    priv->child_type == IPOIB_LEGACY_CHILD) {
2708c2ecf20Sopenharmony_ci			struct ipoib_vlan_delete_work *work;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci			work = kmalloc(sizeof(*work), GFP_KERNEL);
2738c2ecf20Sopenharmony_ci			if (!work) {
2748c2ecf20Sopenharmony_ci				rc = -ENOMEM;
2758c2ecf20Sopenharmony_ci				goto out;
2768c2ecf20Sopenharmony_ci			}
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci			down_write(&ppriv->vlan_rwsem);
2798c2ecf20Sopenharmony_ci			list_del_init(&priv->list);
2808c2ecf20Sopenharmony_ci			up_write(&ppriv->vlan_rwsem);
2818c2ecf20Sopenharmony_ci			work->dev = priv->dev;
2828c2ecf20Sopenharmony_ci			INIT_WORK(&work->work, ipoib_vlan_delete_task);
2838c2ecf20Sopenharmony_ci			queue_work(ipoib_workqueue, &work->work);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci			rc = 0;
2868c2ecf20Sopenharmony_ci			break;
2878c2ecf20Sopenharmony_ci		}
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ciout:
2918c2ecf20Sopenharmony_ci	rtnl_unlock();
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	return rc;
2948c2ecf20Sopenharmony_ci}
295