1219820Sjeff/*
2219820Sjeff * Copyright (c) 2004 Topspin Communications.  All rights reserved.
3219820Sjeff *
4219820Sjeff * This software is available to you under a choice of one of two
5219820Sjeff * licenses.  You may choose to be licensed under the terms of the GNU
6219820Sjeff * General Public License (GPL) Version 2, available from the file
7219820Sjeff * COPYING in the main directory of this source tree, or the
8219820Sjeff * OpenIB.org BSD license below:
9219820Sjeff *
10219820Sjeff *     Redistribution and use in source and binary forms, with or
11219820Sjeff *     without modification, are permitted provided that the following
12219820Sjeff *     conditions are met:
13219820Sjeff *
14219820Sjeff *      - Redistributions of source code must retain the above
15219820Sjeff *        copyright notice, this list of conditions and the following
16219820Sjeff *        disclaimer.
17219820Sjeff *
18219820Sjeff *      - Redistributions in binary form must reproduce the above
19219820Sjeff *        copyright notice, this list of conditions and the following
20219820Sjeff *        disclaimer in the documentation and/or other materials
21219820Sjeff *        provided with the distribution.
22219820Sjeff *
23219820Sjeff * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24219820Sjeff * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25219820Sjeff * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26219820Sjeff * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
27219820Sjeff * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28219820Sjeff * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
29219820Sjeff * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30219820Sjeff * SOFTWARE.
31219820Sjeff */
32219820Sjeff
33219820Sjeff#include <linux/module.h>
34219820Sjeff
35219820Sjeff#include <linux/init.h>
36219820Sjeff#include <linux/slab.h>
37219820Sjeff#include <linux/seq_file.h>
38219820Sjeff
39219820Sjeff#include <asm/uaccess.h>
40219820Sjeff
41219820Sjeff#include "ipoib.h"
42219820Sjeff
43219820Sjeffstatic ssize_t show_parent(struct device *d, struct device_attribute *attr,
44219820Sjeff			   char *buf)
45219820Sjeff{
46219820Sjeff	struct ifnet *dev = to_net_dev(d);
47219820Sjeff	struct ipoib_dev_priv *priv = dev->if_softc;
48219820Sjeff
49219820Sjeff	return sprintf(buf, "%s\n", priv->parent->name);
50219820Sjeff}
51219820Sjeffstatic DEVICE_ATTR(parent, S_IRUGO, show_parent, NULL);
52219820Sjeff
53219820Sjeffint ipoib_vlan_add(struct ifnet *pdev, unsigned short pkey)
54219820Sjeff{
55219820Sjeff	struct ipoib_dev_priv *ppriv, *priv;
56219820Sjeff	char intf_name[IFNAMSIZ];
57219820Sjeff	int result;
58219820Sjeff
59219820Sjeff	if (!capable(CAP_NET_ADMIN))
60219820Sjeff		return -EPERM;
61219820Sjeff
62219820Sjeff	ppriv = pdev->if_softc;
63219820Sjeff
64219820Sjeff	rtnl_lock();
65219820Sjeff	mutex_lock(&ppriv->vlan_mutex);
66219820Sjeff
67219820Sjeff	/*
68219820Sjeff	 * First ensure this isn't a duplicate. We check the parent device and
69219820Sjeff	 * then all of the child interfaces to make sure the Pkey doesn't match.
70219820Sjeff	 */
71219820Sjeff	if (ppriv->pkey == pkey) {
72219820Sjeff		result = -ENOTUNIQ;
73219820Sjeff		priv = NULL;
74219820Sjeff		goto err;
75219820Sjeff	}
76219820Sjeff
77219820Sjeff	list_for_each_entry(priv, &ppriv->child_intfs, list) {
78219820Sjeff		if (priv->pkey == pkey) {
79219820Sjeff			result = -ENOTUNIQ;
80219820Sjeff			priv = NULL;
81219820Sjeff			goto err;
82219820Sjeff		}
83219820Sjeff	}
84219820Sjeff
85219820Sjeff	snprintf(intf_name, sizeof intf_name, "%s.%04x",
86219820Sjeff		 ppriv->dev->name, pkey);
87219820Sjeff	priv = ipoib_intf_alloc(intf_name);
88219820Sjeff	if (!priv) {
89219820Sjeff		result = -ENOMEM;
90219820Sjeff		goto err;
91219820Sjeff	}
92219820Sjeff
93219820Sjeff	priv->max_ib_mtu = ppriv->max_ib_mtu;
94219820Sjeff	/* MTU will be reset when mcast join happens */
95219820Sjeff	priv->dev->mtu   = IPOIB_UD_MTU(priv->max_ib_mtu);
96219820Sjeff	priv->mcast_mtu  = priv->admin_mtu = priv->dev->mtu;
97219820Sjeff	set_bit(IPOIB_FLAG_SUBINTERFACE, &priv->flags);
98219820Sjeff
99219820Sjeff	result = ipoib_set_dev_features(priv, ppriv->ca);
100219820Sjeff	if (result)
101219820Sjeff		goto err;
102219820Sjeff
103219820Sjeff	priv->pkey = pkey;
104219820Sjeff
105219820Sjeff	memcpy(IF_LLADDR(priv->dev), ppriv->dev->dev_addr, INFINIBAND_ALEN);
106219820Sjeff	priv->broadcastaddr[8] = pkey >> 8;
107219820Sjeff	priv->broadcastaddr[9] = pkey & 0xff;
108219820Sjeff
109219820Sjeff	result = ipoib_dev_init(priv->dev, ppriv->ca, ppriv->port);
110219820Sjeff	if (result < 0) {
111219820Sjeff		ipoib_warn(ppriv, "failed to initialize subinterface: "
112219820Sjeff			   "device %s, port %d",
113219820Sjeff			   ppriv->ca->name, ppriv->port);
114219820Sjeff		goto err;
115219820Sjeff	}
116219820Sjeff
117219820Sjeff	result = register_netdevice(priv->dev);
118219820Sjeff	if (result) {
119219820Sjeff		ipoib_warn(priv, "failed to initialize; error %i", result);
120219820Sjeff		goto register_failed;
121219820Sjeff	}
122219820Sjeff
123219820Sjeff	priv->parent = ppriv->dev;
124219820Sjeff
125219820Sjeff	ipoib_create_debug_files(priv->dev);
126219820Sjeff
127219820Sjeff	if (ipoib_cm_add_mode_attr(priv->dev))
128219820Sjeff		goto sysfs_failed;
129219820Sjeff	if (ipoib_add_pkey_attr(priv->dev))
130219820Sjeff		goto sysfs_failed;
131219820Sjeff	if (ipoib_add_umcast_attr(priv->dev))
132219820Sjeff		goto sysfs_failed;
133219820Sjeff
134219820Sjeff	if (device_create_file(&priv->dev->dev, &dev_attr_parent))
135219820Sjeff		goto sysfs_failed;
136219820Sjeff
137219820Sjeff	list_add_tail(&priv->list, &ppriv->child_intfs);
138219820Sjeff
139219820Sjeff	mutex_unlock(&ppriv->vlan_mutex);
140219820Sjeff	rtnl_unlock();
141219820Sjeff
142219820Sjeff	return 0;
143219820Sjeff
144219820Sjeffsysfs_failed:
145219820Sjeff	ipoib_delete_debug_files(priv->dev);
146219820Sjeff	unregister_netdevice(priv->dev);
147219820Sjeff
148219820Sjeffregister_failed:
149219820Sjeff	ipoib_dev_cleanup(priv->dev);
150219820Sjeff
151219820Sjefferr:
152219820Sjeff	mutex_unlock(&ppriv->vlan_mutex);
153219820Sjeff	rtnl_unlock();
154219820Sjeff	if (priv)
155219820Sjeff		free_netdev(priv->dev);
156219820Sjeff
157219820Sjeff	return result;
158219820Sjeff}
159219820Sjeff
160219820Sjeffint ipoib_vlan_delete(struct ifnet *pdev, unsigned short pkey)
161219820Sjeff{
162219820Sjeff	struct ipoib_dev_priv *ppriv, *priv, *tpriv;
163219820Sjeff	struct ifnet *dev = NULL;
164219820Sjeff
165219820Sjeff	if (!capable(CAP_NET_ADMIN))
166219820Sjeff		return -EPERM;
167219820Sjeff
168219820Sjeff	ppriv = pdev->if_softc;
169219820Sjeff
170219820Sjeff	rtnl_lock();
171219820Sjeff	mutex_lock(&ppriv->vlan_mutex);
172219820Sjeff	list_for_each_entry_safe(priv, tpriv, &ppriv->child_intfs, list) {
173219820Sjeff		if (priv->pkey == pkey) {
174219820Sjeff			unregister_netdevice(priv->dev);
175219820Sjeff			ipoib_dev_cleanup(priv->dev);
176219820Sjeff			list_del(&priv->list);
177219820Sjeff			dev = priv->dev;
178219820Sjeff			break;
179219820Sjeff		}
180219820Sjeff	}
181219820Sjeff	mutex_unlock(&ppriv->vlan_mutex);
182219820Sjeff	rtnl_unlock();
183219820Sjeff
184219820Sjeff	if (dev) {
185219820Sjeff		free_netdev(dev);
186219820Sjeff		return 0;
187219820Sjeff	}
188219820Sjeff
189219820Sjeff	return -ENODEV;
190219820Sjeff}
191