* Re: [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Ryan Harper @ 2010-06-21 16:43 UTC (permalink / raw)
To: john cooper; +Cc: virtualization, qemu-devel, kvm
In-Reply-To: <4C1EFDFD.5050907@redhat.com>
* john cooper <john.cooper@redhat.com> [2010-06-21 01:11]:
> Rusty Russell wrote:
> > On Sat, 19 Jun 2010 04:08:02 am Ryan Harper wrote:
> >> Create a new attribute for virtio-blk devices that will fetch the serial number
> >> of the block device. This attribute can be used by udev to create disk/by-id
> >> symlinks for devices that don't have a UUID (filesystem) associated with them.
> >>
> >> ATA_IDENTIFY strings are special in that they can be up to 20 chars long
> >> and aren't required to be NULL-terminated. The buffer is also zero-padded
> >> meaning that if the serial is 19 chars or less that we get a NULL terminated
> >> string. When copying this value into a string buffer, we must be careful to
> >> copy up to the NULL (if it present) and only 20 if it is longer and not to
> >> attempt to NULL terminate; this isn't needed.
> >>
> >> Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
> >> Signed-off-by: john cooper <john.cooper@redhat.com>
> >> ---
> >> drivers/block/virtio_blk.c | 32 ++++++++++++++++++++++++++++++++
> >> 1 files changed, 32 insertions(+), 0 deletions(-)
> >>
> >> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
> >> index 258bc2a..f1ef26f 100644
> >> --- a/drivers/block/virtio_blk.c
> >> +++ b/drivers/block/virtio_blk.c
> >> @@ -281,6 +281,31 @@ static int index_to_minor(int index)
> >> return index << PART_BITS;
> >> }
> >>
> >> +/* Copy serial number from *s to *d. Copy operation terminates on either
> >> + * encountering a nul in *s or after n bytes have been copied, whichever
> >> + * occurs first. *d is not forcibly nul terminated. Return # of bytes copied.
> >> + */
> >> +static inline int serial_sysfs(char *d, char *s, int n)
> >> +{
> >> + char *di = d;
> >> +
> >> + while (*s && n--)
> >> + *d++ = *s++;
> >> + return d - di;
> >> +}
> >> +
> >> +static ssize_t virtblk_serial_show(struct device *dev,
> >> + struct device_attribute *attr, char *buf)
> >> +{
> >> + struct gendisk *disk = dev_to_disk(dev);
> >> + char id_str[VIRTIO_BLK_ID_BYTES];
> >> +
> >> + if (IS_ERR(virtblk_get_id(disk, id_str)))
> >> + return 0;
> >
> > 0? Really? That doesn't seem very informative.
>
> Propagating a prospective error from virtblk_get_id() should
> be possible. Unsure if doing so is more useful from the
> user's perspective compared to just a nul id string.
I'm not sure we can do any thing else here; maybe printk a warning?
Documentation/filesystems/sysfs.txt says that showing attributes should
always return the number of chars put into the buffer; so when there is
an error; zero is the right value to return since we're not filling the
buffer.
>
> >> + return serial_sysfs(buf, id_str, min(VIRTIO_BLK_ID_BYTES, PAGE_SIZE));
> >
> > How about something like this:
> >
> > BUILD_BUG_ON(PAGE_SIZE < VIRTIO_BLK_ID_BYTES + 1);
>
> Agreed, that's a better wrench in the gearworks.
> Note padding buf[] by 1 isn't necessary as indicated
> below.
Yep; that's a good one to take.
>
> > /* id_str is not necessarily nul-terminated! */
> > buf[VIRTIO_BLK_ID_BYTES] = '\0';
> > return virtblk_get_id(disk, buf);
>
> The /sys file is rendered according to the length
> returned from this function and the trailing nul
> is not interpreted in this context. In fact if a
> nul is added and included in the byte count of the
> string it will appear in the /sys file.
Yeah; I like the simplicity; but we do need to know how long the string
is so we can return that value.
>
> Thanks,
>
> -john
>
>
> --
> john.cooper@redhat.com
--
Ryan Harper
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
ryanh@us.ibm.com
^ permalink raw reply
* Re: [Qemu-devel] [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Christoph Hellwig @ 2010-06-21 12:44 UTC (permalink / raw)
To: Ryan Harper; +Cc: john cooper, qemu-devel, kvm, virtualization
In-Reply-To: <1276886283-1571-1-git-send-email-ryanh@us.ibm.com>
On Fri, Jun 18, 2010 at 01:38:02PM -0500, Ryan Harper wrote:
> Create a new attribute for virtio-blk devices that will fetch the serial number
> of the block device. This attribute can be used by udev to create disk/by-id
> symlinks for devices that don't have a UUID (filesystem) associated with them.
>
> ATA_IDENTIFY strings are special in that they can be up to 20 chars long
> and aren't required to be NULL-terminated. The buffer is also zero-padded
> meaning that if the serial is 19 chars or less that we get a NULL terminated
> string. When copying this value into a string buffer, we must be careful to
> copy up to the NULL (if it present) and only 20 if it is longer and not to
> attempt to NULL terminate; this isn't needed.
Why is this virtio-blk specific? In a later mail you mention you want
to use it for udev. So please export this from scsi/libata as well and
we have one proper interface that we can use for all devices.
^ permalink raw reply
* Re: [PATCH for-2.6.35] virtio_net: fix oom handling on tx
From: Michael S. Tsirkin @ 2010-06-21 10:53 UTC (permalink / raw)
To: Rusty Russell
Cc: Shirley Ma, Jiri Pirko, netdev, linux-kernel, virtualization,
Stephen Hemminger, Sridhar Samudrala
In-Reply-To: <201006211953.44724.rusty@rustcorp.com.au>
On Mon, Jun 21, 2010 at 07:53:43PM +0930, Rusty Russell wrote:
> On Mon, 21 Jun 2010 06:03:16 pm Michael S. Tsirkin wrote:
> > On Mon, Jun 21, 2010 at 12:13:49PM +0930, Rusty Russell wrote:
> > > - return NETDEV_TX_BUSY;
> > > + kfree_skb(skb);
> > > + return NETDEV_TX_OK;
> >
> > If we do so, let's increment the dropped counter and/or error counter?
>
> Yep, here's the extra change:
Looks good to me.
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -571,14 +571,16 @@ static netdev_tx_t start_xmit(struct sk_
> /* This can happen with OOM and indirect buffers. */
> if (unlikely(capacity < 0)) {
> if (net_ratelimit()) {
> - if (likely(capacity == -ENOMEM))
> + if (likely(capacity == -ENOMEM)) {
> dev_warn(&dev->dev,
> "TX queue failure: out of memory\n");
> - else
> + } else {
> + dev->stats.tx_fifo_errors++;
> dev_warn(&dev->dev,
> "Unexpected TX queue failure: %d\n",
> capacity);
> }
> + dev->stats.tx_dropped++;
> kfree_skb(skb);
> return NETDEV_TX_OK;
> }
^ permalink raw reply
* Re: [PATCH for-2.6.35] virtio_net: fix oom handling on tx
From: Rusty Russell @ 2010-06-21 10:23 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Shirley Ma, Jiri Pirko, netdev, linux-kernel, virtualization,
Stephen Hemminger, Sridhar Samudrala
In-Reply-To: <20100621083315.GA8665@redhat.com>
On Mon, 21 Jun 2010 06:03:16 pm Michael S. Tsirkin wrote:
> On Mon, Jun 21, 2010 at 12:13:49PM +0930, Rusty Russell wrote:
> > - return NETDEV_TX_BUSY;
> > + kfree_skb(skb);
> > + return NETDEV_TX_OK;
>
> If we do so, let's increment the dropped counter and/or error counter?
Yep, here's the extra change:
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -571,14 +571,16 @@ static netdev_tx_t start_xmit(struct sk_
/* This can happen with OOM and indirect buffers. */
if (unlikely(capacity < 0)) {
if (net_ratelimit()) {
- if (likely(capacity == -ENOMEM))
+ if (likely(capacity == -ENOMEM)) {
dev_warn(&dev->dev,
"TX queue failure: out of memory\n");
- else
+ } else {
+ dev->stats.tx_fifo_errors++;
dev_warn(&dev->dev,
"Unexpected TX queue failure: %d\n",
capacity);
}
+ dev->stats.tx_dropped++;
kfree_skb(skb);
return NETDEV_TX_OK;
}
^ permalink raw reply
* Re: [PATCH for-2.6.35] virtio_net: fix oom handling on tx
From: Michael S. Tsirkin @ 2010-06-21 8:33 UTC (permalink / raw)
To: Rusty Russell
Cc: Shirley Ma, Jiri Pirko, netdev, linux-kernel, virtualization,
Stephen Hemminger, Sridhar Samudrala
In-Reply-To: <201006211213.49600.rusty@rustcorp.com.au>
On Mon, Jun 21, 2010 at 12:13:49PM +0930, Rusty Russell wrote:
> On Fri, 11 Jun 2010 04:33:43 am Michael S. Tsirkin wrote:
> > > > > @@ -572,12 +571,14 @@ again:
> > > > >
> > > > > /* This can happen with OOM and indirect buffers. */
> > > > > if (unlikely(capacity < 0)) {
> > > > > - netif_stop_queue(dev);
> > > > > - dev_warn(&dev->dev, "Unexpected full queue\n");
> > > > > - if (unlikely(!virtqueue_enable_cb(vi->svq))) {
> > > > > - virtqueue_disable_cb(vi->svq);
> > > > > - netif_start_queue(dev);
> > > > > - goto again;
> > > > > + if (net_ratelimit()) {
> > > > > + if (likely(capacity == -ENOMEM))
> > > > > + dev_warn(&dev->dev,
> > > > > + "TX queue failure: out of memory\n");
> > > > > + else
> > > > > + dev_warn(&dev->dev,
> > > > > + "Unexpected TX queue failure: %d\n",
> > > > > + capacity);
> ...
> >
> > Well, I only keep the existing behaviour around.
>
> Actually, it *does* change behavior, as the comment indicates. So let's
> fix the whole thing. AFAICT wth TX_BUSY we'll get called again RSN, and
> that's not really useful for OOM.
>
> This is what I have:
>
> Subject: virtio_net: fix oom handling on tx
> Date: Thu, 10 Jun 2010 18:20:41 +0300
> From: "Michael S. Tsirkin" <mst@redhat.com>
>
> virtio net will never try to overflow the TX ring, so the only reason
> add_buf may fail is out of memory. Thus, we can not stop the
> device until some request completes - there's no guarantee anything
> at all is outstanding.
>
> Make the error message clearer as well: error here does not
> indicate queue full.
>
> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (...and avoid TX_BUSY)
> Cc: stable@kernel.org
> ---
> drivers/net/virtio_net.c | 18 ++++++++++--------
> 1 file changed, 10 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -562,7 +562,6 @@ static netdev_tx_t start_xmit(struct sk_
> struct virtnet_info *vi = netdev_priv(dev);
> int capacity;
>
> -again:
> /* Free up any pending old buffers before queueing new ones. */
> free_old_xmit_skbs(vi);
>
> @@ -571,14 +570,17 @@ again:
>
> /* This can happen with OOM and indirect buffers. */
> if (unlikely(capacity < 0)) {
> - netif_stop_queue(dev);
> - dev_warn(&dev->dev, "Unexpected full queue\n");
> - if (unlikely(!virtqueue_enable_cb(vi->svq))) {
> - virtqueue_disable_cb(vi->svq);
> - netif_start_queue(dev);
> - goto again;
> + if (net_ratelimit()) {
> + if (likely(capacity == -ENOMEM))
> + dev_warn(&dev->dev,
> + "TX queue failure: out of memory\n");
> + else
> + dev_warn(&dev->dev,
> + "Unexpected TX queue failure: %d\n",
> + capacity);
> }
> - return NETDEV_TX_BUSY;
> + kfree_skb(skb);
> + return NETDEV_TX_OK;
If we do so, let's increment the dropped counter and/or error counter?
> }
> virtqueue_kick(vi->svq);
>
^ permalink raw reply
* Re: [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: john cooper @ 2010-06-21 5:51 UTC (permalink / raw)
To: Rusty Russell; +Cc: john.cooper, qemu-devel, kvm, virtualization
In-Reply-To: <201006211122.38156.rusty@rustcorp.com.au>
Rusty Russell wrote:
> On Sat, 19 Jun 2010 04:08:02 am Ryan Harper wrote:
>> Create a new attribute for virtio-blk devices that will fetch the serial number
>> of the block device. This attribute can be used by udev to create disk/by-id
>> symlinks for devices that don't have a UUID (filesystem) associated with them.
>>
>> ATA_IDENTIFY strings are special in that they can be up to 20 chars long
>> and aren't required to be NULL-terminated. The buffer is also zero-padded
>> meaning that if the serial is 19 chars or less that we get a NULL terminated
>> string. When copying this value into a string buffer, we must be careful to
>> copy up to the NULL (if it present) and only 20 if it is longer and not to
>> attempt to NULL terminate; this isn't needed.
>>
>> Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
>> Signed-off-by: john cooper <john.cooper@redhat.com>
>> ---
>> drivers/block/virtio_blk.c | 32 ++++++++++++++++++++++++++++++++
>> 1 files changed, 32 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
>> index 258bc2a..f1ef26f 100644
>> --- a/drivers/block/virtio_blk.c
>> +++ b/drivers/block/virtio_blk.c
>> @@ -281,6 +281,31 @@ static int index_to_minor(int index)
>> return index << PART_BITS;
>> }
>>
>> +/* Copy serial number from *s to *d. Copy operation terminates on either
>> + * encountering a nul in *s or after n bytes have been copied, whichever
>> + * occurs first. *d is not forcibly nul terminated. Return # of bytes copied.
>> + */
>> +static inline int serial_sysfs(char *d, char *s, int n)
>> +{
>> + char *di = d;
>> +
>> + while (*s && n--)
>> + *d++ = *s++;
>> + return d - di;
>> +}
>> +
>> +static ssize_t virtblk_serial_show(struct device *dev,
>> + struct device_attribute *attr, char *buf)
>> +{
>> + struct gendisk *disk = dev_to_disk(dev);
>> + char id_str[VIRTIO_BLK_ID_BYTES];
>> +
>> + if (IS_ERR(virtblk_get_id(disk, id_str)))
>> + return 0;
>
> 0? Really? That doesn't seem very informative.
Propagating a prospective error from virtblk_get_id() should
be possible. Unsure if doing so is more useful from the
user's perspective compared to just a nul id string.
>> + return serial_sysfs(buf, id_str, min(VIRTIO_BLK_ID_BYTES, PAGE_SIZE));
>
> How about something like this:
>
> BUILD_BUG_ON(PAGE_SIZE < VIRTIO_BLK_ID_BYTES + 1);
Agreed, that's a better wrench in the gearworks.
Note padding buf[] by 1 isn't necessary as indicated
below.
> /* id_str is not necessarily nul-terminated! */
> buf[VIRTIO_BLK_ID_BYTES] = '\0';
> return virtblk_get_id(disk, buf);
The /sys file is rendered according to the length
returned from this function and the trailing nul
is not interpreted in this context. In fact if a
nul is added and included in the byte count of the
string it will appear in the /sys file.
Thanks,
-john
--
john.cooper@redhat.com
^ permalink raw reply
* Re: [PATCH 2/2] Remove virtio_blk VBID ioctl
From: john cooper @ 2010-06-21 5:07 UTC (permalink / raw)
To: Rusty Russell; +Cc: john.cooper, qemu-devel, kvm, virtualization
In-Reply-To: <201006211100.19719.rusty@rustcorp.com.au>
Rusty Russell wrote:
> On Sat, 19 Jun 2010 04:08:03 am Ryan Harper wrote:
>> With the availablility of a sysfs device attribute for examining disk serial
>> numbers the ioctl is no longer needed. The user-space changes for this aren't
>> upstream yet so we don't have any users to worry about.
>
> If John Cooper acks this, I'll push it to Linus immediately.
Actually I'm the one who suggested removing it.
The code in question was only intended as example
usage of accessing the s/n data in the driver, for
the /sys interface under discussion back then.
That effort subsequently stalled and Ryan had
recently picked it up. As such I believe this
overshadows the general need for an ioctl. Even if
for some reason an ioctl would be justified going
forward, a more usage friendly form would be better.
So let's just drop it for now as the corresponding
qemu-side code hasn't been merged.
> Unfortunately we offered this interface in 2.6.34, and we're now removing it.
> That's unpleasant.
Indeed. This entire effort, aside from being an exercise
in protracted agony, probably violates a Rube Goldberg
patent.
Thanks,
-john
--
john.cooper@redhat.com
^ permalink raw reply
* Re: [PATCH for-2.6.35] virtio_net: fix oom handling on tx
From: Rusty Russell @ 2010-06-21 2:43 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Shirley Ma, Jiri Pirko, netdev, linux-kernel, virtualization,
Stephen Hemminger, Sridhar Samudrala
In-Reply-To: <20100610190343.GC4044@redhat.com>
On Fri, 11 Jun 2010 04:33:43 am Michael S. Tsirkin wrote:
> > > > @@ -572,12 +571,14 @@ again:
> > > >
> > > > /* This can happen with OOM and indirect buffers. */
> > > > if (unlikely(capacity < 0)) {
> > > > - netif_stop_queue(dev);
> > > > - dev_warn(&dev->dev, "Unexpected full queue\n");
> > > > - if (unlikely(!virtqueue_enable_cb(vi->svq))) {
> > > > - virtqueue_disable_cb(vi->svq);
> > > > - netif_start_queue(dev);
> > > > - goto again;
> > > > + if (net_ratelimit()) {
> > > > + if (likely(capacity == -ENOMEM))
> > > > + dev_warn(&dev->dev,
> > > > + "TX queue failure: out of memory\n");
> > > > + else
> > > > + dev_warn(&dev->dev,
> > > > + "Unexpected TX queue failure: %d\n",
> > > > + capacity);
...
>
> Well, I only keep the existing behaviour around.
Actually, it *does* change behavior, as the comment indicates. So let's
fix the whole thing. AFAICT wth TX_BUSY we'll get called again RSN, and
that's not really useful for OOM.
This is what I have:
Subject: virtio_net: fix oom handling on tx
Date: Thu, 10 Jun 2010 18:20:41 +0300
From: "Michael S. Tsirkin" <mst@redhat.com>
virtio net will never try to overflow the TX ring, so the only reason
add_buf may fail is out of memory. Thus, we can not stop the
device until some request completes - there's no guarantee anything
at all is outstanding.
Make the error message clearer as well: error here does not
indicate queue full.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> (...and avoid TX_BUSY)
Cc: stable@kernel.org
---
drivers/net/virtio_net.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -562,7 +562,6 @@ static netdev_tx_t start_xmit(struct sk_
struct virtnet_info *vi = netdev_priv(dev);
int capacity;
-again:
/* Free up any pending old buffers before queueing new ones. */
free_old_xmit_skbs(vi);
@@ -571,14 +570,17 @@ again:
/* This can happen with OOM and indirect buffers. */
if (unlikely(capacity < 0)) {
- netif_stop_queue(dev);
- dev_warn(&dev->dev, "Unexpected full queue\n");
- if (unlikely(!virtqueue_enable_cb(vi->svq))) {
- virtqueue_disable_cb(vi->svq);
- netif_start_queue(dev);
- goto again;
+ if (net_ratelimit()) {
+ if (likely(capacity == -ENOMEM))
+ dev_warn(&dev->dev,
+ "TX queue failure: out of memory\n");
+ else
+ dev_warn(&dev->dev,
+ "Unexpected TX queue failure: %d\n",
+ capacity);
}
- return NETDEV_TX_BUSY;
+ kfree_skb(skb);
+ return NETDEV_TX_OK;
}
virtqueue_kick(vi->svq);
^ permalink raw reply
* Re: [PATCH 2/2] Remove virtio_blk VBID ioctl
From: Ryan Harper @ 2010-06-21 2:30 UTC (permalink / raw)
To: Rusty Russell; +Cc: john cooper, qemu-devel, kvm, virtualization
In-Reply-To: <201006211100.19719.rusty@rustcorp.com.au>
* Rusty Russell <rusty@rustcorp.com.au> [2010-06-20 20:31]:
> On Sat, 19 Jun 2010 04:08:03 am Ryan Harper wrote:
> > With the availablility of a sysfs device attribute for examining disk serial
> > numbers the ioctl is no longer needed. The user-space changes for this aren't
> > upstream yet so we don't have any users to worry about.
>
> If John Cooper acks this, I'll push it to Linus immediately.
>
> Unfortunately we offered this interface in 2.6.34, and we're now removing it.
> That's unpleasant.
Yes; well. There's a story as there always is. John can tell it better
than I, but it goes something like:
John cooked up some patches, one of which was an example use of the
serial string including a VBID ioctl. No one got around to doing a
sysfs interface and somehow the ioctl side in virtio-blk got picked up.
Working with what was available, I pushed some patches to linux-hotplug
to get this whole virtio-blk serial and disk/by-id symlinks working and
was met with: why does a new kernel driver have an ioctl interface and
we don't want to collect additional single-use binaries in the udev
tree.
So now, the sysfs serial attribute patch and with it, no need and no
users of ioctl.
*whew*
>
> Thanks,
> Rusty.
> PS. John should have been cc'd on these patches!
He's cc'ed on the others, just not on this removal one.
I need to learn git-send-email better, I explicitly added him as --cc on
when sending; next time I'll look closer at the headers during
--dry-run.
--
Ryan Harper
Software Engineer; Linux Technology Center
IBM Corp., Austin, Tx
ryanh@us.ibm.com
^ permalink raw reply
* Re: [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Rusty Russell @ 2010-06-21 1:52 UTC (permalink / raw)
To: Ryan Harper; +Cc: john cooper, qemu-devel, kvm, virtualization
In-Reply-To: <1276886283-1571-1-git-send-email-ryanh@us.ibm.com>
On Sat, 19 Jun 2010 04:08:02 am Ryan Harper wrote:
> Create a new attribute for virtio-blk devices that will fetch the serial number
> of the block device. This attribute can be used by udev to create disk/by-id
> symlinks for devices that don't have a UUID (filesystem) associated with them.
>
> ATA_IDENTIFY strings are special in that they can be up to 20 chars long
> and aren't required to be NULL-terminated. The buffer is also zero-padded
> meaning that if the serial is 19 chars or less that we get a NULL terminated
> string. When copying this value into a string buffer, we must be careful to
> copy up to the NULL (if it present) and only 20 if it is longer and not to
> attempt to NULL terminate; this isn't needed.
>
> Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
> Signed-off-by: john cooper <john.cooper@redhat.com>
> ---
> drivers/block/virtio_blk.c | 32 ++++++++++++++++++++++++++++++++
> 1 files changed, 32 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
> index 258bc2a..f1ef26f 100644
> --- a/drivers/block/virtio_blk.c
> +++ b/drivers/block/virtio_blk.c
> @@ -281,6 +281,31 @@ static int index_to_minor(int index)
> return index << PART_BITS;
> }
>
> +/* Copy serial number from *s to *d. Copy operation terminates on either
> + * encountering a nul in *s or after n bytes have been copied, whichever
> + * occurs first. *d is not forcibly nul terminated. Return # of bytes copied.
> + */
> +static inline int serial_sysfs(char *d, char *s, int n)
> +{
> + char *di = d;
> +
> + while (*s && n--)
> + *d++ = *s++;
> + return d - di;
> +}
> +
> +static ssize_t virtblk_serial_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct gendisk *disk = dev_to_disk(dev);
> + char id_str[VIRTIO_BLK_ID_BYTES];
> +
> + if (IS_ERR(virtblk_get_id(disk, id_str)))
> + return 0;
0? Really? That doesn't seem very informative.
> + return serial_sysfs(buf, id_str, min(VIRTIO_BLK_ID_BYTES, PAGE_SIZE));
How about something like this:
BUILD_BUG_ON(PAGE_SIZE < VIRTIO_BLK_ID_BYTES + 1);
/* id_str is not necessarily nul-terminated! */
buf[VIRTIO_BLK_ID_BYTES] = '\0';
return virtblk_get_id(disk, buf);
Thanks,
Rusty.
^ permalink raw reply
* Re: [PATCH 2/2] Remove virtio_blk VBID ioctl
From: Rusty Russell @ 2010-06-21 1:30 UTC (permalink / raw)
To: Ryan Harper; +Cc: john cooper, qemu-devel, kvm, virtualization
In-Reply-To: <1276886283-1571-2-git-send-email-ryanh@us.ibm.com>
On Sat, 19 Jun 2010 04:08:03 am Ryan Harper wrote:
> With the availablility of a sysfs device attribute for examining disk serial
> numbers the ioctl is no longer needed. The user-space changes for this aren't
> upstream yet so we don't have any users to worry about.
If John Cooper acks this, I'll push it to Linus immediately.
Unfortunately we offered this interface in 2.6.34, and we're now removing it.
That's unpleasant.
Thanks,
Rusty.
PS. John should have been cc'd on these patches!
^ permalink raw reply
* Re: [Qemu-devel] [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Blue Swirl @ 2010-06-19 15:59 UTC (permalink / raw)
To: Ulrich Drepper; +Cc: kvm, john cooper, qemu-devel, virtualization
In-Reply-To: <4C1CA2DB.2080502@redhat.com>
On Sat, Jun 19, 2010 at 10:58 AM, Ulrich Drepper <drepper@redhat.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 06/19/2010 01:24 AM, Blue Swirl wrote:
>>> +static inline int serial_sysfs(char *d, char *s, int n)
>>> +{
>>> + char *di = d;
>>
>> I'd change this to:
>> static inline ssize_t serial_sysfs(char *d, const char *s, ssize_t n)
>> {
>> const char *di = d;
>>
>>> +
>>> + while (*s && n--)
>>> + *d++ = *s++;
>>> + return d - di;
>
> I would guess you mean
>
> char *const di = d;
>
> Quite different and doesn't elicit warnings from the compiler.
I had actually confused the types of d and s. So either your version
or the original for this line.
The return statement assumes that ptrdiff_t is equal to ssize_t (or
int in the original version) but I guess that's OK.
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [Qemu-devel] [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Ulrich Drepper @ 2010-06-19 10:58 UTC (permalink / raw)
To: Blue Swirl; +Cc: kvm, john cooper, qemu-devel, virtualization
In-Reply-To: <AANLkTin7WwGhL-O4ENzG6u-2q0pYiLpyH1tfzLUxK4PQ@mail.gmail.com>
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1
On 06/19/2010 01:24 AM, Blue Swirl wrote:
>> +static inline int serial_sysfs(char *d, char *s, int n)
>> +{
>> + char *di = d;
>
> I'd change this to:
> static inline ssize_t serial_sysfs(char *d, const char *s, ssize_t n)
> {
> const char *di = d;
>
>> +
>> + while (*s && n--)
>> + *d++ = *s++;
>> + return d - di;
I would guess you mean
char *const di = d;
Quite different and doesn't elicit warnings from the compiler.
- --
➧ Ulrich Drepper ➧ Red Hat, Inc. ➧ 444 Castro St ➧ Mountain View, CA ❖
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.10 (GNU/Linux)
iEYEARECAAYFAkwcotsACgkQ2ijCOnn/RHQLowCgqmoJCFjfh/ySP4/PQAWKmKJ9
rAwAn1O1L3hb2nzd7altEWT/PXg8oecx
=YkfO
-----END PGP SIGNATURE-----
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [Qemu-devel] [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Blue Swirl @ 2010-06-19 8:24 UTC (permalink / raw)
To: Ryan Harper; +Cc: john cooper, qemu-devel, kvm, virtualization
In-Reply-To: <1276886283-1571-1-git-send-email-ryanh@us.ibm.com>
On Fri, Jun 18, 2010 at 6:38 PM, Ryan Harper <ryanh@us.ibm.com> wrote:
> Create a new attribute for virtio-blk devices that will fetch the serial number
> of the block device. This attribute can be used by udev to create disk/by-id
> symlinks for devices that don't have a UUID (filesystem) associated with them.
>
> ATA_IDENTIFY strings are special in that they can be up to 20 chars long
> and aren't required to be NULL-terminated. The buffer is also zero-padded
> meaning that if the serial is 19 chars or less that we get a NULL terminated
> string. When copying this value into a string buffer, we must be careful to
> copy up to the NULL (if it present) and only 20 if it is longer and not to
> attempt to NULL terminate; this isn't needed.
>
> Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
> Signed-off-by: john cooper <john.cooper@redhat.com>
> ---
> drivers/block/virtio_blk.c | 32 ++++++++++++++++++++++++++++++++
> 1 files changed, 32 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
> index 258bc2a..f1ef26f 100644
> --- a/drivers/block/virtio_blk.c
> +++ b/drivers/block/virtio_blk.c
> @@ -281,6 +281,31 @@ static int index_to_minor(int index)
> return index << PART_BITS;
> }
>
> +/* Copy serial number from *s to *d. Copy operation terminates on either
> + * encountering a nul in *s or after n bytes have been copied, whichever
> + * occurs first. *d is not forcibly nul terminated. Return # of bytes copied.
> + */
> +static inline int serial_sysfs(char *d, char *s, int n)
> +{
> + char *di = d;
I'd change this to:
static inline ssize_t serial_sysfs(char *d, const char *s, ssize_t n)
{
const char *di = d;
> +
> + while (*s && n--)
> + *d++ = *s++;
> + return d - di;
> +}
> +
> +static ssize_t virtblk_serial_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct gendisk *disk = dev_to_disk(dev);
> + char id_str[VIRTIO_BLK_ID_BYTES];
> +
> + if (IS_ERR(virtblk_get_id(disk, id_str)))
> + return 0;
> + return serial_sysfs(buf, id_str, min(VIRTIO_BLK_ID_BYTES, PAGE_SIZE));
> +}
> +DEVICE_ATTR(serial, S_IRUGO, virtblk_serial_show, NULL);
> +
> static int __devinit virtblk_probe(struct virtio_device *vdev)
> {
> struct virtio_blk *vblk;
> @@ -445,8 +470,15 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
>
>
> add_disk(vblk->disk);
> + err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial);
> + if (err)
> + goto out_del_disk;
> +
> return 0;
>
> +out_del_disk:
> + del_gendisk(vblk->disk);
> + blk_cleanup_queue(vblk->disk->queue);
> out_put_disk:
> put_disk(vblk->disk);
> out_mempool:
> --
> 1.6.3.3
>
>
>
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* [PATCH 2/2] Remove virtio_blk VBID ioctl
From: Ryan Harper @ 2010-06-18 18:38 UTC (permalink / raw)
To: virtualization; +Cc: qemu-devel, kvm
In-Reply-To: <1276886283-1571-1-git-send-email-ryanh@us.ibm.com>
With the availablility of a sysfs device attribute for examining disk serial
numbers the ioctl is no longer needed. The user-space changes for this aren't
upstream yet so we don't have any users to worry about.
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
---
drivers/block/virtio_blk.c | 10 ----------
1 files changed, 0 insertions(+), 10 deletions(-)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index f1ef26f..9e382dd 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -225,16 +225,6 @@ static int virtblk_ioctl(struct block_device *bdev, fmode_t mode,
struct gendisk *disk = bdev->bd_disk;
struct virtio_blk *vblk = disk->private_data;
- if (cmd == 0x56424944) { /* 'VBID' */
- void __user *usr_data = (void __user *)data;
- char id_str[VIRTIO_BLK_ID_BYTES];
- int err;
-
- err = virtblk_get_id(disk, id_str);
- if (!err && copy_to_user(usr_data, id_str, VIRTIO_BLK_ID_BYTES))
- err = -EFAULT;
- return err;
- }
/*
* Only allow the generic SCSI ioctls if the host can support it.
*/
--
1.6.3.3
^ permalink raw reply related
* [PATCH 1/2] Add 'serial' attribute to virtio-blk devices
From: Ryan Harper @ 2010-06-18 18:38 UTC (permalink / raw)
To: virtualization; +Cc: john cooper, qemu-devel, kvm
Create a new attribute for virtio-blk devices that will fetch the serial number
of the block device. This attribute can be used by udev to create disk/by-id
symlinks for devices that don't have a UUID (filesystem) associated with them.
ATA_IDENTIFY strings are special in that they can be up to 20 chars long
and aren't required to be NULL-terminated. The buffer is also zero-padded
meaning that if the serial is 19 chars or less that we get a NULL terminated
string. When copying this value into a string buffer, we must be careful to
copy up to the NULL (if it present) and only 20 if it is longer and not to
attempt to NULL terminate; this isn't needed.
Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
Signed-off-by: john cooper <john.cooper@redhat.com>
---
drivers/block/virtio_blk.c | 32 ++++++++++++++++++++++++++++++++
1 files changed, 32 insertions(+), 0 deletions(-)
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 258bc2a..f1ef26f 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -281,6 +281,31 @@ static int index_to_minor(int index)
return index << PART_BITS;
}
+/* Copy serial number from *s to *d. Copy operation terminates on either
+ * encountering a nul in *s or after n bytes have been copied, whichever
+ * occurs first. *d is not forcibly nul terminated. Return # of bytes copied.
+ */
+static inline int serial_sysfs(char *d, char *s, int n)
+{
+ char *di = d;
+
+ while (*s && n--)
+ *d++ = *s++;
+ return d - di;
+}
+
+static ssize_t virtblk_serial_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gendisk *disk = dev_to_disk(dev);
+ char id_str[VIRTIO_BLK_ID_BYTES];
+
+ if (IS_ERR(virtblk_get_id(disk, id_str)))
+ return 0;
+ return serial_sysfs(buf, id_str, min(VIRTIO_BLK_ID_BYTES, PAGE_SIZE));
+}
+DEVICE_ATTR(serial, S_IRUGO, virtblk_serial_show, NULL);
+
static int __devinit virtblk_probe(struct virtio_device *vdev)
{
struct virtio_blk *vblk;
@@ -445,8 +470,15 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)
add_disk(vblk->disk);
+ err = device_create_file(disk_to_dev(vblk->disk), &dev_attr_serial);
+ if (err)
+ goto out_del_disk;
+
return 0;
+out_del_disk:
+ del_gendisk(vblk->disk);
+ blk_cleanup_queue(vblk->disk->queue);
out_put_disk:
put_disk(vblk->disk);
out_mempool:
--
1.6.3.3
^ permalink raw reply related
* patch staging-hv-fix-hv_utils-module-to-properly-autoload.patch added to gregkh-2.6 tree
From: gregkh @ 2010-06-18 16:53 UTC (permalink / raw)
To: haiyangz, devel, gregkh, hjanssen, linux-kernel, stable,
virtualization
In-Reply-To: <1FB5E1D5CA062146B38059374562DF7266B8931E@TK5EX14MBXC128.redmond.corp.microsoft.com>
This is a note to let you know that I've just added the patch titled
Subject: Staging: hv: fix hv_utils module to properly autoload
to my gregkh-2.6 tree. Its filename is
staging-hv-fix-hv_utils-module-to-properly-autoload.patch
This tree can be found at
http://www.kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/patches/
From haiyangz@microsoft.com Wed May 19 08:56:34 2010
From: Haiyang Zhang <haiyangz@microsoft.com>
Date: Wed, 19 May 2010 15:56:28 +0000
Subject: Staging: hv: fix hv_utils module to properly autoload
To: "'linux-kernel@vger.kernel.org'" <linux-kernel@vger.kernel.org>, "'devel@driverdev.osuosl.org'" <devel@driverdev.osuosl.org>, "'virtualization@lists.osdl.org'" <virtualization@lists.osdl.org>, "'gregkh@suse.de'" <gregkh@suse.de>
Cc: Hank Janssen <hjanssen@microsoft.com>
Message-ID: <1FB5E1D5CA062146B38059374562DF7266B8931E@TK5EX14MBXC128.redmond.corp.microsoft.com>
From: Haiyang Zhang <haiyangz@microsoft.com>
Added autoloading based on pci id and dmi strings.
Signed-off-by: Haiyang Zhang <haiyangz@microsoft.com>
Signed-off-by: Hank Janssen <hjanssen@microsoft.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/staging/hv/hv_utils.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
--- a/drivers/staging/hv/hv_utils.c
+++ b/drivers/staging/hv/hv_utils.c
@@ -24,6 +24,8 @@
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/reboot.h>
+#include <linux/dmi.h>
+#include <linux/pci.h>
#include "logging.h"
#include "osd.h"
@@ -251,10 +253,36 @@ static void heartbeat_onchannelcallback(
DPRINT_EXIT(VMBUS);
}
+static const struct pci_device_id __initconst
+hv_utils_pci_table[] __maybe_unused = {
+ { PCI_DEVICE(0x1414, 0x5353) }, /* Hyper-V emulated VGA controller */
+ { 0 }
+};
+MODULE_DEVICE_TABLE(pci, hv_utils_pci_table);
+
+
+static const struct dmi_system_id __initconst
+hv_utils_dmi_table[] __maybe_unused = {
+ {
+ .ident = "Hyper-V",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+ DMI_MATCH(DMI_BOARD_NAME, "Virtual Machine"),
+ },
+ },
+ { },
+};
+MODULE_DEVICE_TABLE(dmi, hv_utils_dmi_table);
+
+
static int __init init_hyperv_utils(void)
{
printk(KERN_INFO "Registering HyperV Utility Driver\n");
+ if (!dmi_check_system(hv_utils_dmi_table))
+ return -ENODEV;
+
hv_cb_utils[HV_SHUTDOWN_MSG].channel->OnChannelCallback =
&shutdown_onchannelcallback;
hv_cb_utils[HV_SHUTDOWN_MSG].callback = &shutdown_onchannelcallback;
^ permalink raw reply
* Re: [Qemu-devel] Virtualization at Plumbers 2010 - Time to submit your proposals!
From: Luiz Capitulino @ 2010-06-18 13:36 UTC (permalink / raw)
To: Jes Sorensen
Cc: Zhang, Yanmin, xen-devel, KVM General, jvrao, Peter Zijlstra,
QEMU Developers, virtualization
In-Reply-To: <4C1B5FE4.8020207@redhat.com>
On Fri, 18 Jun 2010 14:00:36 +0200
Jes Sorensen <Jes.Sorensen@redhat.com> wrote:
> - QMP and Spice
I think we're going to discuss most QMP related subjects in the KVM forum,
but I'm open to suggestions.
^ permalink raw reply
* Virtualization at Plumbers 2010 - Time to submit your proposals!
From: Jes Sorensen @ 2010-06-18 12:00 UTC (permalink / raw)
To: KVM General, QEMU Developers, xen-devel, virtualization
Cc: Peter Zijlstra, Ingo Molnar, Zhang, Yanmin, jvrao
Hi,
I would like to remind people about the Virtualization track at Linux
Plumbers Conference 2010, held in Cambridge, MA, November 3-5, 2010.
Please note the deadline for submissions is July 19, 2010.
LPC is particular well suited for technical presentations, work in
progress and subjects that needs discussion and collaboration between
communities (kernel, desktop/gfx, virtualization, etc.), so if you have
a contentious issue you would like to bring to a wider audience, this is
the ideal place to do it!
Note that this track is focusing on general Linux Virtualization, it
is not hypervisor specific. Submissions related to Xen, KVM, VMware,
containers, etc. are encouraged. Subjects could include:
- Linux Kernel Virtualization enhancements
- QEMU
- IO performance work
- Device management, hotplug
- NUMA awareness
- Live migration
- Support for new hardware features, and/or provide guest access to
these features.
- Device emulation
- Para-virtual enhancements: special filesystems, PMU, Windows
drivers, etc.
- Debugging and analysis tools
- Containers
- QMP and Spice
- Virtualization management, user interfaces, and desktop integration
(GNOME, KDE, etc)
If you have a subject you would like to present, please submit it as
soon as possible, and no later than July 19th. Please see the full Call
For Papers at http://www.linuxplumbersconf.org/2010/ for how to submit.
You may also want to list your ideas at the LPC Virtualization session
wiki page at http://wiki.linuxplumbersconf.org/2010:virtualization
Hope to see you in Cambridge in November!
Jes
^ permalink raw reply
* [PATCH 3/3] VDP commandline interface
From: Jens Osterkamp @ 2010-06-17 13:57 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1276783031-10239-1-git-send-email-jens@linux.vnet.ibm.com>
This patch implements the command line interface to control the VDP module.
In station role, it allows to register a new VSI (guest interface) profile
and query its state. In bridge role, it allows to query the state of all
registered profiles.
With lldptool it is possible to set the role from default "station" to
"bridge".
The configuration of the role for a port is saved to lldpads config file.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
Makefile.am | 12 +-
include/lldp_vdp_clif.h | 38 +++++
include/lldp_vdp_cmds.h | 44 ++++++
lldp_vdp_clif.c | 136 ++++++++++++++++
lldp_vdp_cmds.c | 392 +++++++++++++++++++++++++++++++++++++++++++++++
lldptool.c | 2 +
6 files changed, 619 insertions(+), 5 deletions(-)
create mode 100644 include/lldp_vdp_clif.h
create mode 100644 include/lldp_vdp_cmds.h
create mode 100644 lldp_vdp_clif.c
create mode 100644 lldp_vdp_cmds.c
diff --git a/Makefile.am b/Makefile.am
index e2ecce4..d16f9f2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -39,7 +39,8 @@ lldpad_include_HEADERS = include/dcb_types.h include/dcbtool.h \
include/dcb_osdep.h include/clif.h include/lldp_dcbx_cmds.h include/common.h \
include/lldpad.h include/os.h include/includes.h include/lldp_mand_cmds.h \
include/clif_msgs.h include/lldp_basman_cmds.h include/lldp_8023_cmds.h \
-include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h
+include/lldp_med_cmds.h include/lldp_dcbx_cfg.h include/lldp_evb_cmds.h \
+include/lldp_vdp_cmds.h
noinst_HEADERS = include/config.h include/ctrl_iface.h \
include/dcb_driver_if_types.h include/dcb_driver_interface.h \
@@ -49,7 +50,7 @@ include/event_iface.h include/messages.h include/parse_cli.h include/version.h \
include/lldptool_cli.h include/list.h \
include/lldp_mand_clif.h include/lldp_basman_clif.h include/lldp_med_clif.h \
include/lldp_8023_clif.h include/lldp_dcbx_clif.h include/lldptool.h \
-include/lldp_rtnl.h include/lldp_evb_clif.h
+include/lldp_rtnl.h include/lldp_evb_clif.h include/lldp_vdp_clif.h
lldpad_SOURCES = lldpad.c config.c drv_cfg.c ctrl_iface.c event_iface.c eloop.c \
common.c os_unix.c lldp_dcbx_cmds.c log.c lldpad_shm.c \
@@ -66,20 +67,21 @@ lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \
lldp_util.c include/lldp_util.h \
lldp_mand.c include/lldp_mand.h \
lldp_mand_cmds.c lldp_basman_cmds.c lldp_8023_cmds.c lldp_med_cmds.c \
-lldp_evb_cmds.c \
+lldp_evb_cmds.c lldp_vdp_cmds.c \
lldp_tlv.c include/lldp_tlv.h \
lldp_basman.c include/lldp_basman.h \
lldp_med.c include/lldp_med.h \
lldp_8023.c include/lldp_8023.h \
lldp_evb.c include/lldp_evb.h \
-lldp_vdp.c include/lldp_vdp.h
+lldp_vdp.c include/lldp_vdp.h \
+lldp_vdp_cmds.h
dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \
$(lldpad_include_HEADERS) $(noinst_HEADERS)
lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \
lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \
-lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \
+lldp_dcbx_clif.c lldp_evb_clif.c lldp_vdp_clif.c $(lldpad_include_HEADERS) \
$(noinst_HEADERS)
nltest_SOURCES = nltest.c nltest.h
diff --git a/include/lldp_vdp_clif.h b/include/lldp_vdp_clif.h
new file mode 100644
index 0000000..9fcfacb
--- /dev/null
+++ b/include/lldp_vdp_clif.h
@@ -0,0 +1,38 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_CLIF_H
+#define _LLDP_VDP_CLIF_H
+
+struct lldp_module *vdp_cli_register(void);
+void vdp_cli_unregister(struct lldp_module *);
+int vdp_print_tlv(u32, u16, char *);
+
+#define VDP_BUF_SIZE 256
+
+#define ARG_VDP_MODE "mode"
+#define ARG_VDP_ROLE "role"
+
+#endif
diff --git a/include/lldp_vdp_cmds.h b/include/lldp_vdp_cmds.h
new file mode 100644
index 0000000..5e64c27
--- /dev/null
+++ b/include/lldp_vdp_cmds.h
@@ -0,0 +1,44 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_CMDS_H
+#define _LLDP_VDP_CMDS_H
+
+struct arg_handlers *vdp_get_arg_handlers();
+
+enum {
+ MODE = 0,
+ MGRID,
+ TYPEID,
+ TYPEIDVERSION,
+ INSTANCEID,
+ MAC,
+ VLAN,
+};
+
+#define VAL_STATION "station"
+#define VAL_BRIDGE "bridge"
+
+#endif
diff --git a/lldp_vdp_clif.c b/lldp_vdp_clif.c
new file mode 100644
index 0000000..7d5f5b3
--- /dev/null
+++ b/lldp_vdp_clif.c
@@ -0,0 +1,136 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "includes.h"
+#include "common.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include "lldp_mod.h"
+#include "lldptool.h"
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "lldp_vdp_clif.h"
+
+void vdp_print_cfg_tlv(u16, char *info);
+int vdp_print_help();
+
+u32 vdp_lookup_tlv_name(char *tlvid_str);
+
+static const struct lldp_mod_ops vdp_ops_clif = {
+ .lldp_mod_register = vdp_cli_register,
+ .lldp_mod_unregister = vdp_cli_unregister,
+ .print_tlv = vdp_print_tlv,
+ .lookup_tlv_name = vdp_lookup_tlv_name,
+ .print_help = vdp_print_help,
+};
+
+struct type_name_info vdp_tlv_names[] = {
+ { (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE,
+ "VDP protocol configuration",
+ "vdp", vdp_print_cfg_tlv },
+ { INVALID_TLVID, NULL, NULL }
+};
+
+int vdp_print_help()
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (tn->key && strlen(tn->key) && tn->name) {
+ printf(" %s", tn->key);
+ if (strlen(tn->key)+3 <= 8)
+ printf("\t");
+ printf("\t: %s\n", tn->name);
+ }
+ tn++;
+ }
+
+ return 0;
+}
+
+struct lldp_module *vdp_cli_register(void)
+{
+ struct lldp_module *mod;
+
+ mod = malloc(sizeof(*mod));
+ if (!mod) {
+ fprintf(stderr, "failed to malloc module data\n");
+ return NULL;
+ }
+ mod->id = LLDP_MOD_VDP;
+ mod->ops = &vdp_ops_clif;
+
+ return mod;
+}
+
+void vdp_cli_unregister(struct lldp_module *mod)
+{
+ free(mod);
+}
+
+void vdp_print_cfg_tlv(u16 len, char *info)
+{
+ /* TODO: this should print out all associated VSI mac/vlan pairs */
+ printf("This should print out all associated VSI mac/vlan pairs !\n");
+
+ return;
+}
+
+/* return 1: if it printed the TLV
+ * 0: if it did not
+ */
+int vdp_print_tlv(u32 tlvid, u16 len, char *info)
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (tlvid == tn->type) {
+ printf("%s\n", tn->name);
+ if (tn->print_info) {
+ printf("\t");
+ tn->print_info(len-4, info);
+ }
+ return 1;
+ }
+ tn++;
+ }
+
+ return 0;
+}
+
+u32 vdp_lookup_tlv_name(char *tlvid_str)
+{
+ struct type_name_info *tn = &vdp_tlv_names[0];
+
+ while (tn->type != INVALID_TLVID) {
+ if (!strcasecmp(tn->key, tlvid_str))
+ return tn->type;
+ tn++;
+ }
+ return INVALID_TLVID;
+}
+
diff --git a/lldp_vdp_cmds.c b/lldp_vdp_cmds.c
new file mode 100644
index 0000000..56ba376
--- /dev/null
+++ b/lldp_vdp_cmds.c
@@ -0,0 +1,392 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "includes.h"
+#include "common.h"
+#include <stdio.h>
+#include <syslog.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+#include "lldpad.h"
+#include "ctrl_iface.h"
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "lldp_mand_clif.h"
+#include "lldp_vdp_clif.h"
+#include "lldp_vdp_cmds.h"
+#include "lldp/ports.h"
+#include "libconfig.h"
+#include "config.h"
+#include "clif_msgs.h"
+#include "lldp/states.h"
+
+static int get_arg_tlvtxenable(struct cmd *, char *, char *, char *);
+static int set_arg_tlvtxenable(struct cmd *, char *, char *, char *);
+
+static int get_arg_mode(struct cmd *, char *, char *, char *);
+static int set_arg_mode(struct cmd *, char *, char *, char *);
+
+static int get_arg_role(struct cmd *, char *, char *, char *);
+static int set_arg_role(struct cmd *, char *, char *, char *);
+
+static struct arg_handlers arg_handlers[] = {
+ { ARG_VDP_MODE, get_arg_mode, set_arg_mode },
+ { ARG_VDP_ROLE, get_arg_role, set_arg_role },
+ { ARG_TLVTXENABLE, get_arg_tlvtxenable, set_arg_tlvtxenable },
+ { NULL }
+};
+
+static int get_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char *s;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE:
+ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s",
+ TLVID_PREFIX, cmd->tlvid, arg);
+
+ if (get_config_setting(cmd->ifname, arg_path, (void *)&value,
+ CONFIG_TYPE_BOOL))
+ value = false;
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (value)
+ s = VAL_YES;
+ else
+ s = VAL_NO;
+
+ sprintf(obuf, "%02x%s%04x%s", strlen(arg), arg, strlen(s), s);
+
+ return cmd_success;
+}
+
+static int set_arg_tlvtxenable(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (!strcasecmp(argvalue, VAL_YES))
+ value = 1;
+ else if (!strcasecmp(argvalue, VAL_NO))
+ value = 0;
+ else
+ return cmd_invalid;
+
+ snprintf(arg_path, sizeof(arg_path), "%s%08x.%s", TLVID_PREFIX,
+ cmd->tlvid, arg);
+
+ if (set_cfg(cmd->ifname, arg_path, (void *)&value, CONFIG_TYPE_BOOL))
+ return cmd_failed;
+
+ return cmd_success;
+}
+
+static int get_arg_mode(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ char *s, *t;
+ char arg_path[VDP_BUF_SIZE];
+ struct vsi_profile *np;
+ struct vdp_data *vd;
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ s = t = malloc(VDP_BUF_SIZE);
+ if (!s)
+ return cmd_invalid;
+ memset(s, 0, VDP_BUF_SIZE);
+
+ vd = vdp_data(cmd->ifname);
+ if (!vd) {
+ printf("%s(%i): vdp_data for %s not found !\n", __func__, __LINE__,
+ cmd->ifname);
+ return cmd_invalid;
+ }
+
+ LIST_FOREACH(np, &vd->profile_head, profile) {
+ PRINT_PROFILE(t, np);
+ }
+
+ sprintf(obuf, "%02x%s%04x%s", strlen(arg), arg, strlen(s), s);
+
+ return cmd_success;
+}
+
+static void str2instance(struct vsi_profile *profile, char *buffer)
+{
+ int i, j = 0;
+ u8 mac[50];
+
+ for(i=0; i <= strlen(buffer); i++) {
+ if (buffer[i] == '-') {
+ continue;
+ }
+
+ if ((sscanf(&buffer[i], "%02x", &profile->instance[j]) == 1) ||
+ (sscanf(&buffer[i], "%02X", &profile->instance[j]) == 1)) {
+ i++;
+ j++;
+ continue;
+ }
+ }
+
+}
+
+/* INSTANCE_STRLEN = strlen("fa9b7fff-b0a0-4893e-beef4ff18f8f") */
+#define INSTANCE_STRLEN 32
+
+int instance2str(const u8 *p, char *dst, size_t size)
+{
+ if (dst && size > INSTANCE_STRLEN) {
+ snprintf(dst, size, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x",
+ p[0], p[1], p[2], p[3],
+ p[4], p[5], p[6], p[7],
+ p[8], p[9], p[10], p[11],
+ p[12], p[13], p[14], p[15]);
+ return 0;
+ }
+ return -1;
+}
+
+static void vdp_fill_profile(struct vsi_profile *profile, char *buffer, int field)
+{
+ int id;
+
+ switch(field) {
+ case MODE:
+ profile->mode = atoi(buffer);
+ break;
+ case MGRID:
+ profile->mgrid = atoi(buffer);
+ break;
+ case TYPEID:
+ id = atoi(buffer);
+ profile->id[0] = (u8) (id & 0xff);
+ profile->id[1] = (u8) ((id >> 8) & 0xff);
+ profile->id[2] = (u8) ((id >> 16) & 0xff);
+ break;
+ case TYPEIDVERSION:
+ profile->version = atoi(buffer);
+ break;
+ case INSTANCEID:
+ str2instance(profile, buffer);
+ break;
+ case MAC:
+ str2mac(buffer, &profile->mac[0], MAC_ADDR_LEN);
+ break;
+ case VLAN:
+ profile->vlan = atoi(buffer);
+ break;
+ default:
+ printf("Unknown field in buffer !\n");
+ break;
+ }
+}
+
+static struct vsi_profile *vdp_parse_mode_line(char * argvalue)
+{
+ int i, arglen, field;
+ char *cmdstring, *buf;
+ char *buffer;
+ struct vsi_profile *profile;
+
+ profile = malloc(sizeof(struct vsi_profile));
+ if (!profile)
+ return NULL;
+ memset(profile, 0, sizeof(struct vsi_profile));
+
+ arglen = strlen(argvalue);
+ cmdstring = argvalue;
+ buffer = malloc(arglen);
+ if (!buffer)
+ goto out_free;
+ buf = buffer;
+ field = 0;
+
+ for (i=0; i <= arglen; i++) {
+ *buffer = *cmdstring;
+
+ if ((*cmdstring == ',') || (*cmdstring == '\0')) {
+ *buffer++ = '\0';
+ vdp_fill_profile(profile, buf, field);
+ field++;
+ buffer = buf;
+ memset(buffer, 0, arglen);
+ cmdstring++;
+ continue;
+ }
+
+ buffer++;
+ cmdstring++;
+ }
+
+ return profile;
+
+out_free:
+ free(profile);
+ return NULL;
+}
+
+static int set_arg_mode(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int i, arglen;
+ char *cmdstring, *buf;
+ char *buffer;
+ struct vsi_profile *profile, *p;
+
+ arglen = strlen(argvalue);
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_EVB_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ profile = vdp_parse_mode_line(argvalue);
+ profile->port = port_find_by_name(cmd->ifname);
+
+ p = vdp_add_profile(profile);
+
+ if (!p) {
+ free(profile);
+ return cmd_invalid;
+ }
+
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ vdp_vsi_sm_station(p);
+
+ return cmd_success;
+}
+
+static int get_arg_role(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char *s;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_gettlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE:
+ if (get_config_setting(cmd->ifname, ARG_VDP_ROLE, (void *)&value,
+ CONFIG_TYPE_INT))
+ value = VDP_ROLE_STATION;
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (value == VDP_ROLE_BRIDGE)
+ s = VAL_BRIDGE;
+ else
+ s = VAL_STATION;
+
+ sprintf(obuf, "%02x%s%04x%s", strlen(arg), arg, strlen(s), s);
+
+ return cmd_success;
+}
+
+static int set_arg_role(struct cmd *cmd, char *arg, char *argvalue,
+ char *obuf)
+{
+ int value;
+ char arg_path[VDP_BUF_SIZE];
+
+ if (cmd->cmd != cmd_settlv)
+ return cmd_invalid;
+
+ switch (cmd->tlvid) {
+ case (LLDP_MOD_VDP << 8) | LLDP_VDP_SUBTYPE:
+ break;
+ case INVALID_TLVID:
+ return cmd_invalid;
+ default:
+ return cmd_not_applicable;
+ }
+
+ if (!strcasecmp(argvalue, VAL_BRIDGE))
+ value = VDP_ROLE_BRIDGE;
+ else if (!strcasecmp(argvalue, VAL_STATION))
+ value = VDP_ROLE_STATION;
+ else
+ return cmd_invalid;
+
+ if (set_cfg(cmd->ifname, ARG_VDP_ROLE, (void *)&value, CONFIG_TYPE_INT))
+ return cmd_failed;
+
+ return cmd_success;
+}
+
+
+struct arg_handlers *vdp_get_arg_handlers()
+{
+ return &arg_handlers[0];
+}
diff --git a/lldptool.c b/lldptool.c
index a06a279..97b097d 100644
--- a/lldptool.c
+++ b/lldptool.c
@@ -40,6 +40,7 @@
#include "lldp_8023_clif.h"
#include "lldp_dcbx_clif.h"
#include "lldp_evb_clif.h"
+#include "lldp_vdp_clif.h"
#include "lldptool.h"
#include "lldptool_cli.h"
#include "lldp_mod.h"
@@ -158,6 +159,7 @@ struct lldp_module *(*register_tlv_table[])(void) = {
med_cli_register,
dcbx_cli_register,
evb_cli_register,
+ vdp_cli_register,
NULL,
};
--
1.7.1
^ permalink raw reply related
* [PATCH 2/3] implementation of vdp
From: Jens Osterkamp @ 2010-06-17 13:57 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1276783031-10239-1-git-send-email-jens@linux.vnet.ibm.com>
This patch contains an initial implemention of VDP as specified in IEEE 802.1Qbg.
VDP serves as the upper layer protocol (ULP) for TLVs communicated via the
ECP protocol.
For this it registers as a new module in lldpad. The VDP module supports a
station and a bridge role. As a station, new VSI (virtual station interface)
profiles can be registered to the VDP module using lldptool or libvirt (at a
later point in time). These profiles are then announced to an adjacent
bridge. Transmitted profiles are processed to the desired state by the VDP
station state machine.
As a bridge, the VDP module waits for new profiles received in TLVs by ECP.
The received profiles are processed to the desired state by a VDP bridge
state machine.
The patch still contains a lot of debug code to allow analysis of VDP
protocol behavior.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
Makefile.am | 8 +-
ecp/ecp_rx.c | 9 +-
ecp/ecp_tx.c | 36 ++-
include/lldp.h | 1 +
lldp_vdp.c | 1140 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
lldpad.c | 2 +
6 files changed, 1190 insertions(+), 6 deletions(-)
create mode 100644 lldp_vdp.c
diff --git a/Makefile.am b/Makefile.am
index 693e57d..e2ecce4 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -71,16 +71,16 @@ lldp_tlv.c include/lldp_tlv.h \
lldp_basman.c include/lldp_basman.h \
lldp_med.c include/lldp_med.h \
lldp_8023.c include/lldp_8023.h \
-lldp_evb.c include/lldp_evb.h
-
-
+lldp_evb.c include/lldp_evb.h \
+lldp_vdp.c include/lldp_vdp.h
dcbtool_SOURCES = dcbtool.c clif.c dcbtool_cmds.c parse_cli.l \
$(lldpad_include_HEADERS) $(noinst_HEADERS)
lldptool_SOURCES = lldptool.c clif.c lldptool_cmds.c common.c os_unix.c \
lldp_mand_clif.c lldp_basman_clif.c lldp_med_clif.c lldp_8023_clif.c \
-lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) $(noinst_HEADERS)
+lldp_dcbx_clif.c lldp_evb_clif.c $(lldpad_include_HEADERS) \
+$(noinst_HEADERS)
nltest_SOURCES = nltest.c nltest.h
diff --git a/ecp/ecp_rx.c b/ecp/ecp_rx.c
index d8c050f..cc54377 100644
--- a/ecp/ecp_rx.c
+++ b/ecp/ecp_rx.c
@@ -420,7 +420,14 @@ void ecp_rx_ProcessFrame(struct port * port)
tlv_offset += sizeof(*tlv_head_ptr) + tlv_length;
if (tlv->type == TYPE_127) { /* private TLV */
- /* TODO: give VSI TLV to VDP */
+ /* give VSI TLV to VDP */
+ if (!vdp_indicate(port, tlv, ecp_hdr->mode))
+ tlv_stored = true;
+ else {
+ /* TODO: put it in a list and try again later until
+ * timer and retries have expired */
+ tlv_stored = false;
+ }
}
if ((tlv->type != TYPE_0) && !tlv_stored) {
diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c
index d31edba..78a61ca 100644
--- a/ecp/ecp_tx.c
+++ b/ecp/ecp_tx.c
@@ -98,6 +98,13 @@ bool ecp_build_ECPDU(struct port *port, int mode)
struct vdp_data *vd;
struct vsi_profile *p;
+ vd = vdp_data(port->ifname);
+ if (!vd) {
+ printf("%s(%i): could not find vdp_data for %s !\n", __func__, __LINE__,
+ port->ifname);
+ goto error;
+ }
+
if (port->ecp.tx.frameout) {
free(port->ecp.tx.frameout);
port->ecp.tx.frameout = NULL;
@@ -145,7 +152,34 @@ bool ecp_build_ECPDU(struct port *port, int mode)
datasize += sizeof(struct ecp_hdr);
fb_offset += sizeof(struct ecp_hdr);
- /* TODO: create tlvs from profiles here */
+ /* create packed_tlvs for all profiles on this interface */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if(!p) {
+ printf("%s(%i): list vd->profile_head empty !\n", __func__, __LINE__);
+ continue;
+ }
+
+ if (p->localChange != mode) {
+ printf("%s(%i): not sending out profile !\n", __func__, __LINE__);
+ continue;
+ }
+
+ ptlv = vdp_gettlv(port, p);
+
+ if(!ptlv) {
+ printf("%s(%i): ptlv not created !\n", __func__, __LINE__);
+ continue;
+ }
+
+ if (ptlv) {
+ if ((ptlv->size+fb_offset) > ETH_MAX_DATA_LEN)
+ goto error;
+ memcpy(port->ecp.tx.frameout+fb_offset,
+ ptlv->tlv, ptlv->size);
+ datasize += ptlv->size;
+ fb_offset += ptlv->size;
+ }
+ }
/* The End TLV marks the end of the LLDP PDU */
ptlv = pack_end_tlv();
diff --git a/include/lldp.h b/include/lldp.h
index 21347b0..7af4149 100644
--- a/include/lldp.h
+++ b/include/lldp.h
@@ -190,6 +190,7 @@ enum {
/* IEEE 802.1Qbg subtype */
#define LLDP_EVB_SUBTYPE 0
+#define LLDP_VDP_SUBTYPE 0
/* forwarding mode */
#define LLDP_EVB_CAPABILITY_FORWARD_STANDARD (1 << 7)
diff --git a/lldp_vdp.c b/lldp_vdp.c
new file mode 100644
index 0000000..a511a43
--- /dev/null
+++ b/lldp_vdp.c
@@ -0,0 +1,1140 @@
+/*******************************************************************************
+
+ implementation of VDP according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include <net/if.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <linux/if_bridge.h>
+#include "lldp.h"
+#include "lldp_vdp.h"
+#include "ecp/ecp.h"
+#include "lldp_evb.h"
+#include "messages.h"
+#include "config.h"
+#include "common.h"
+#include "lldp_vdp_clif.h"
+#include "lldp_vdp_cmds.h"
+
+/* vdp_data - searches vdp_data in the list of modules for this port
+ * @ifname: interface name to search for
+ *
+ * returns vdp_data on success, NULL on error
+ *
+ * searches the list of user_data for the VDP module user_data.
+ */
+struct vdp_data *vdp_data(char *ifname)
+{
+ struct vdp_user_data *ud;
+ struct vdp_data *vd = NULL;
+
+ ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP);
+ if (ud) {
+ LIST_FOREACH(vd, &ud->head, entry) {
+ if (!strncmp(ifname, vd->ifname, IFNAMSIZ))
+ return vd;
+ }
+ }
+ return NULL;
+}
+
+/* vdp_free_tlv - free tlv in vdp_data
+ * @vd: vdp_data
+ *
+ * no return value
+ *
+ * frees up tlv in vdp_data. used in vdp_free_data.
+ */
+static void vdp_free_tlv(struct vdp_data *vd)
+{
+ if (vd) {
+ FREE_UNPKD_TLV(vd, vdp);
+ }
+}
+
+/* vdp_free_data - frees up vdp data
+ * @ud: user data structure
+ *
+ * no return value
+ *
+ * removes vd_structure from the user_data list. frees up tlv in vdp_data.
+ * used in vdp_unregister.
+ */
+static void vdp_free_data(struct vdp_user_data *ud)
+{
+ struct vdp_data *vd;
+ if (ud) {
+ while (!LIST_EMPTY(&ud->head)) {
+ vd = LIST_FIRST(&ud->head);
+ LIST_REMOVE(vd, entry);
+ vdp_free_tlv(vd);
+ free(vd);
+ }
+ }
+}
+
+/* vdp_print_profile - print a vsi profile
+ * @profile: profile to print
+ *
+ * no return value
+ *
+ * prints the contents of a profile first to a string using the PRINT_PROFILE
+ * macro, and then to the screen. Used for debug purposes.
+ */
+static inline void vdp_print_profile(struct vsi_profile *profile)
+{
+ char *s, *t;
+
+ s = t = malloc(VDP_BUF_SIZE);
+
+ if (!s) {
+ printf("%s(%i): unable to allocate string !\n", __func__, __LINE__);
+ }
+
+ PRINT_PROFILE(t, profile);
+
+ printf("profile: %s\n", s);
+
+ free(s);
+}
+
+/* vdp_somethingChangedLocal - set flag if profile has changed
+ * @profile: profile to set the flag for
+ * @mode: mode to set the flag to
+ *
+ * no return value
+ *
+ * set the localChange flag with a mode to indicate a profile has changed.
+ * used next time when a ecpdu with profiles is sent out.
+ */
+void vdp_somethingChangedLocal(struct vsi_profile *profile, int mode)
+{
+ profile->localChange = mode;
+}
+
+/* vdp_ackTimer_expired - checks for expired ack timer
+ * @profile: profile to be checked
+ *
+ * returns true or false
+ *
+ * returns value of profile->ackTimerExpired, true if ack timer has expired,
+ * false otherwise.
+ */
+static bool vdp_ackTimer_expired(struct vsi_profile *profile)
+{
+ return profile->ackTimerExpired;
+}
+
+/* vdp_timeout_handler - handles the ack timer expiry
+ * @eloop_data: data structure of event loop
+ * @user_ctx: user context, profile here
+ *
+ * no return value
+ *
+ * called when the VDP ack timer has expired. sets a flag and calls the VDP
+ * state machine.
+ */
+void vdp_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct vsi_profile *profile;
+
+ profile = (struct vsi_profile *) user_ctx;
+
+ profile->ackTimerExpired = true;
+
+ printf("%s(%i)-%s: timer expired\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ vdp_vsi_sm_station(profile);
+}
+
+/* vdp_stop_ackTimer - stop the VDP ack timer
+ * @profile: profile to process
+ *
+ * returns the number of removed handlers
+ *
+ * stops the VDP ack timer. used when a ack frame for the profile has been
+ * received.
+ */
+static int vdp_stop_ackTimer(struct vsi_profile *profile)
+{
+ printf("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ return eloop_cancel_timeout(vdp_timeout_handler, NULL, (void *) profile);
+}
+
+/* vdp_start_ackTimer - starts the VDP ack timer
+ * @profile: profile to process
+ *
+ * returns 0 on success, -1 on error
+ *
+ * starts the ack timer when a frame has been sent out.
+ */
+static int vdp_start_ackTimer(struct vsi_profile *profile)
+{
+ unsigned int secs, usecs;
+
+ profile->ackTimerExpired = false;
+
+ secs = VDP_TRANSMISSION_TIMER / VDP_TRANSMISSION_DIVIDER;
+ usecs = VDP_TRANSMISSION_TIMER % VDP_TRANSMISSION_DIVIDER;
+
+ printf("%s(%i)-%s: starting timer\n", __func__, __LINE__,
+ profile->port->ifname);
+
+ return eloop_register_timeout(secs, usecs, vdp_timeout_handler, NULL, (void *) profile);
+}
+
+/* vdp_vsi_change_station_state - changes the VDP station sm state
+ * @profile: profile to process
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * actually changes the state of the profile
+ */
+void vdp_vsi_change_station_state(struct vsi_profile *profile, u8 newstate)
+{
+ switch(newstate) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ assert((profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_UNASSOCIATED));
+ break;
+ case VSI_ASSOCIATED:
+ assert(profile->state == VSI_ASSOC_PROCESSING);
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ assert(profile->state == VSI_UNASSOCIATED);
+ break;
+ case VSI_PREASSOCIATED:
+ assert(profile->state == VSI_PREASSOC_PROCESSING);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ assert((profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_EXIT:
+ assert((profile->state == VSI_ASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOC_PROCESSING) ||
+ (profile->state == VSI_DEASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ default:
+ printf("ERROR: The VDP station State Machine is broken!\n");
+ break;
+ }
+
+ printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state], vsi_states[newstate]);
+
+ profile->state = newstate;
+}
+
+/* vdp_vsi_set_station_state - sets the vdp sm station state
+ * @profile: profile to process
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool vdp_vsi_set_station_state(struct vsi_profile *profile)
+{
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ if ((profile->mode == VDP_MODE_PREASSOCIATE) ||
+ (profile->mode == VDP_MODE_PREASSOCIATE_WITH_RR)) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_ASSOC_PROCESSING:
+ if (profile->ackReceived) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOCIATED);
+ return true;
+ } else if (!profile->ackReceived && vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ return false;
+ case VSI_ASSOCIATED:
+ if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_DEASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_PREASSOC_PROCESSING:
+ if (profile->ackReceived) {
+ vdp_vsi_change_station_state(profile, VSI_PREASSOCIATED);
+ return true;
+ } else if (vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ case VSI_PREASSOCIATED:
+ if (profile->mode == VDP_MODE_DEASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ }
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_station_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_DEASSOC_PROCESSING:
+ if ((profile->ackReceived) || vdp_ackTimer_expired(profile)) {
+ vdp_vsi_change_station_state(profile, VSI_EXIT);
+ return true;
+ }
+ return false;
+ case VSI_EXIT:
+ return false;
+ default:
+ printf("ERROR: The VSI RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "");
+ return false;
+ }
+}
+
+/* vdp_vsi_sm_station - state machine for vdp station role
+ * @profile: profile for which the state is processed
+ *
+ * no return value
+ *
+ * runs the state machine for the station role of VDP.
+ */
+void vdp_vsi_sm_station(struct vsi_profile *profile)
+{
+ vdp_vsi_set_station_state(profile);
+ do {
+ printf("%s(%i)-%s: station - %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state]);
+
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ ecp_somethingChangedLocal(profile->port, VDP_PROFILE_REQ);
+ ecp_tx_run_sm(profile->port);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_ASSOCIATED:
+ vdp_stop_ackTimer(profile);
+ /* TODO:
+ * vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (!vsiError) vsistate=ASSOCIATED */
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ /* send out profile */
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ ecp_somethingChangedLocal(profile->port, VDP_PROFILE_REQ);
+ ecp_tx_run_sm(profile->port);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_PREASSOCIATED:
+ profile->ackReceived = false;
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_NOCHANGE);
+ vdp_stop_ackTimer(profile);
+ /* TODO vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (!vsiError) vsistate=PREASSOCIATED */
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ vdp_somethingChangedLocal(profile, VDP_PROFILE_REQ);
+ vdp_start_ackTimer(profile);
+ break;
+ case VSI_EXIT:
+ /* TODO: something went wrong, remove this profile */
+ break;
+ default:
+ printf("ERROR: The VSI RX station State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "");
+ }
+ } while (vdp_vsi_set_station_state(profile) == true);
+
+}
+
+/* vdp_vsi_change_bridge_state - changes the VDP bridge sm state
+ * @profile: profile to process
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * actually changes the state of the profile
+ */
+static void vdp_vsi_change_bridge_state(struct vsi_profile *profile, u8 newstate)
+{
+ switch(newstate) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_ASSOCIATED:
+ assert(profile->state == VSI_ASSOC_PROCESSING);
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_PREASSOCIATED:
+ assert(profile->state == VSI_PREASSOC_PROCESSING);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ assert((profile->state == VSI_UNASSOCIATED) ||
+ (profile->state == VSI_PREASSOCIATED) ||
+ (profile->state == VSI_ASSOCIATED));
+ break;
+ case VSI_EXIT:
+ assert((profile->state == VSI_DEASSOC_PROCESSING) ||
+ (profile->state == VSI_PREASSOC_PROCESSING) ||
+ (profile->state == VSI_ASSOC_PROCESSING));
+ break;
+ default:
+ printf("ERROR: The VDP bridge State Machine is broken!\n");
+ break;
+ }
+ profile->state = newstate;
+}
+
+/* vdp_vsi_set_bridge_state - sets the vdp sm bridge state
+ * @profile: profile to process
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool vdp_vsi_set_bridge_state(struct vsi_profile *profile)
+{
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ if ((profile->mode == VDP_MODE_DEASSOCIATE)) /* || (INACTIVE)) */ {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_ASSOC_PROCESSING:
+ /* TODO: handle error case
+ if (!vsiError) ||
+ (vsiError && vsiState == Assoc) {
+ */
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOCIATED);
+ return true;
+ }
+ return false;
+ case VSI_ASSOCIATED:
+ if (profile->mode == VDP_MODE_ASSOCIATE) /* || ( INACTIVE )*/ {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_PREASSOC_PROCESSING:
+ if (profile->response != VDP_RESPONSE_SUCCESS) {
+ vdp_vsi_change_bridge_state(profile, VSI_EXIT);
+ return true;
+ }
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOCIATED);
+ return false;
+ case VSI_PREASSOCIATED:
+ if (profile->mode == VDP_MODE_ASSOCIATE) {
+ vdp_vsi_change_bridge_state(profile, VSI_ASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_DEASSOCIATE ) {
+ vdp_vsi_change_bridge_state(profile, VSI_DEASSOC_PROCESSING);
+ return true;
+ } else if (profile->mode == VDP_MODE_PREASSOCIATE ) {
+ vdp_vsi_change_bridge_state(profile, VSI_PREASSOC_PROCESSING);
+ return true;
+ }
+ return false;
+ case VSI_DEASSOC_PROCESSING:
+ vdp_vsi_change_bridge_state(profile, VSI_EXIT);
+ return false;
+ case VSI_EXIT:
+ return false;
+ default:
+ printf("ERROR: The VSI RX State Machine (bridge) is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "");
+ return false;
+ }
+}
+
+/* vdp_vsi_sm_bridge - state machine for vdp bridge role
+ * @profile: profile for which the state is processed
+ *
+ * no return value
+ *
+ * runs the state machine for the bridge role of VDP.
+ */
+static void vdp_vsi_sm_bridge(struct vsi_profile *profile)
+{
+ vdp_vsi_set_bridge_state(profile);
+ do {
+ printf("%s(%i)-%s: bridge - %s\n", __func__, __LINE__,
+ profile->port->ifname, vsi_states[profile->state]);
+ switch(profile->state) {
+ case VSI_UNASSOCIATED:
+ break;
+ case VSI_ASSOC_PROCESSING:
+ /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (vsiError)
+ * txTLV(Assoc NACK)
+ * else
+ * txTLV(Assoc ACK) */
+ break;
+ case VSI_ASSOCIATED:
+ break;
+ case VSI_PREASSOC_PROCESSING:
+ /* TODO: vsiError = ProcRxandSetCfg(remoteTLV, localtlv, vsistate);
+ * if (vsiError)
+ * txTLV(PreAssoc NACK)
+ * else
+ * txTLV(PreAssoc ACK) */
+ /* for now, we always succeed */
+ profile->response = VDP_RESPONSE_SUCCESS;
+ printf("%s(%i)-%s: framein %p, sizein %i\n", __func__, __LINE__,
+ profile->port->ifname, profile->port->ecp.rx.framein,
+ profile->port->ecp.rx.sizein);
+ ecp_rx_send_ack_frame(profile->port);
+ break;
+ case VSI_PREASSOCIATED:
+ printf("%s(%i)-%s: \n", __func__, __LINE__, profile->port->ifname);
+ break;
+ case VSI_DEASSOC_PROCESSING:
+ /* TODO: txTLV(DeAssoc ACK) */
+ break;
+ case VSI_EXIT:
+ /* TODO: something went wrong, remove this profile */
+ break;
+ default:
+ printf("ERROR: The VSI RX bridge State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "");
+ }
+ } while (vdp_vsi_set_bridge_state(profile) == true);
+
+}
+
+/*
+ * vdp_print_vsi_tlv - print the raw contents of a VSI TLV
+ * @tlv: the unpacked tlv which gets printed
+ *
+ * No return value
+ *
+ * used for protocol debug purposes
+ */
+static void vdp_print_vsi_tlv(struct unpacked_tlv *tlv)
+{
+ int i;
+
+ printf("### %s:type %i, length %i, info:\n", __func__, tlv->type, tlv->length);
+
+ for (i=0; i < tlv->length; i++) {
+ printf("%02x ", tlv->info[i]);
+ if (!((i+1) % 16))
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+/*
+ * vdp_validate_tlv - validates vsi tlvs
+ * @vdp: decoded vsi tlv
+ *
+ * Returns 0 on success, 1 on error
+ *
+ * checks the contents of an already decoded vsi tlv for inconsistencies
+ */
+static int vdp_validate_tlv(struct tlv_info_vdp *vdp)
+{
+ if (ntoh24(vdp->oui) != OUI_IEEE_8021Qbg) {
+ printf("vdp->oui %06x \n", vdp->oui);
+ goto out_err;
+ }
+
+ if (vdp->sub != LLDP_VDP_SUBTYPE) {
+ printf("vdp->sub %02x \n", vdp->sub);
+ goto out_err;
+ }
+
+ if ((vdp->mode < VDP_MODE_PREASSOCIATE) ||
+ (vdp->mode > VDP_MODE_DEASSOCIATE)) {
+ printf("Unknown mode %02x in vsi tlv !\n", vdp->mode);
+ goto out_err;
+ }
+
+ if ((vdp->response < VDP_RESPONSE_SUCCESS) ||
+ (vdp->response > VDP_RESPONSE_OUT_OF_SYNC)) {
+ printf("Unknown response %02x \n", vdp->response);
+ goto out_err;
+ }
+
+ if (vdp->format != VDP_MACVLAN_FORMAT_1) {
+ printf("Unknown format %02x in vsi tlv !\n", vdp->format);
+ goto out_err;
+ }
+
+ if (vdp->entries != 1) {
+ printf("Multiple entries %02x in vsi tlv !\n", vdp->entries);
+ goto out_err;
+ }
+
+ return 0;
+
+out_err:
+ return 1;
+}
+
+/*
+ * vdp_indicate - receive VSI TLVs from ECP
+ * @port: the port on which the tlv was received
+ * @tlv: the unpacked tlv to receive
+ * @ecp_mode: the mode under which the tlv was received (ACK or REQ)
+ *
+ * Returns 0 on success
+ *
+ * receives a vsi tlv and creates a profile. Take appropriate action
+ * depending on the role of the (receive) port
+ */
+int vdp_indicate(struct port *port, struct unpacked_tlv *tlv, int ecp_mode)
+{
+ char *s, *t;
+ struct vdp_data *vd;
+ struct tlv_info_vdp *vdp;
+ struct vsi_profile *p, *profile;
+
+ vd = vdp_data(port->ifname);
+ if (!vd) {
+ fprintf(stderr, "### %s:%s does not exist !\n", __func__,
+ port->ifname);
+ goto out_err;
+ }
+
+ vdp = malloc(sizeof(struct tlv_info_vdp));
+
+ if (!vdp) {
+ printf("%s(%i): unable to allocate vdp !\n", __func__, __LINE__);
+ goto out_err;
+ }
+
+ memset(vdp, 0, sizeof(struct tlv_info_vdp));
+ memcpy(vdp, tlv->info, tlv->length);
+
+ if (vdp_validate_tlv(vdp)) {
+ printf("%s(%i): Invalid TLV received !\n", __func__, __LINE__);
+ goto out_err;
+ }
+
+ profile = malloc(sizeof(struct vsi_profile));
+
+ if (!profile) {
+ printf("%s(%i): unable to allocate profile !\n", __func__, __LINE__);
+ goto out_vdp;
+ }
+
+ memset(profile, 0, sizeof(struct vsi_profile));
+
+ profile->mode = vdp->mode;
+ profile->response = vdp->response;
+
+ profile->mgrid = vdp->mgrid;
+ memcpy(&profile->id, &vdp->id, 3);
+ profile->version = vdp->version;
+ memcpy(&profile->instance, &vdp->instance, 16);
+ memcpy(&profile->mac, &vdp->mac_vlan.mac, MAC_ADDR_LEN);
+ profile->vlan = vdp->mac_vlan.vlan;
+
+ profile->port = port;
+ printf("%s(%i):\n", __func__, __LINE__);
+
+ if (vd->role == VDP_ROLE_STATION) {
+ /* do we have the profile already ? */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (vdp_profile_equal(p, profile)) {
+ printf("%s(%i): station: profile found, localChange %i ackReceived %i!\n",
+ __func__, __LINE__, p->localChange, p->ackReceived);
+
+ if (ecp_mode == ECP_ACK)
+ p->ackReceived = true;
+
+ vdp_vsi_sm_station(p);
+ } else {
+ printf("%s(%i): station: profile not found !\n", __func__, __LINE__);
+ /* ignore profile */
+ }
+ }
+ }
+
+ if (vd->role == VDP_ROLE_BRIDGE) {
+ /* do we have the profile already ? */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (vdp_profile_equal(p, profile)) {
+ break;
+ }
+ }
+
+ if (p) {
+ printf("%s(%i): bridge: profile found !\n", __func__, __LINE__);
+ } else {
+ printf("%s(%i): bridge: profile not found !\n", __func__, __LINE__);
+ /* put it in the list */
+ profile->state = VSI_UNASSOCIATED;
+ LIST_INSERT_HEAD(&vd->profile_head, profile, profile );
+ }
+
+ vdp_vsi_sm_bridge(profile);
+ }
+
+ return 0;
+
+out_vdp:
+ free(vdp);
+out_err:
+ printf("%s(%i): error !\n", __func__, __LINE__);
+ return 1;
+
+}
+
+/*
+ * vdp_bld_vsi_tlv - build the VDP VSI TLV
+ * @vd: vdp_data structure for this port
+ * @profile: profile the vsi tlv is created from
+ *
+ * Returns 0 on success, ENOMEM otherwise
+ *
+ * creates a vdp structure from an existing profile
+ */
+static int vdp_bld_vsi_tlv(struct vdp_data *vd, struct vsi_profile *profile)
+{
+ int rc = 0;
+ int i;
+ struct unpacked_tlv *tlv = NULL;
+ struct tlv_info_vdp vdp;
+
+ FREE_UNPKD_TLV(vd, vdp);
+
+ memset(&vdp, 0, sizeof(vdp));
+
+ hton24(vdp.oui, OUI_IEEE_8021Qbg);
+ vdp.sub = LLDP_VDP_SUBTYPE;
+ vdp.mode = profile->mode;
+ vdp.response = 0;
+ vdp.mgrid = profile->mgrid;
+ memcpy(&vdp.id,&profile->id, 3);
+ vdp.version = profile->version;
+ memcpy(&vdp.instance,&profile->instance, 16);
+ vdp.format = VDP_MACVLAN_FORMAT_1;
+ vdp.entries = 1;
+ memcpy(&vdp.mac_vlan.mac,&profile->mac, MAC_ADDR_LEN);
+ vdp.mac_vlan.vlan = profile->vlan;
+
+ tlv = create_tlv();
+ if (!tlv)
+ goto out_err;
+
+ tlv->type = ORG_SPECIFIC_TLV;
+ tlv->length = sizeof(vdp);
+ tlv->info = (u8 *)malloc(tlv->length);
+ if(!tlv->info) {
+ free(tlv);
+ tlv = NULL;
+ rc = ENOMEM;
+ goto out_err;
+ }
+ memcpy(tlv->info, &vdp, tlv->length);
+
+ vd->vdp = tlv;
+
+out_err:
+ return rc;
+}
+
+/* vdp_bld_tlv - builds a tlv from a profile
+ * @vd: vdp_data structure for this port
+ * @profile: profile the vsi tlv is created from
+ *
+ * returns 0 on success, != 0 on error
+ *
+ * wrapper function around vdp_bld_vsi_tlv. adds some checks and calls
+ * vdp_bld_vsi_tlv.
+*/
+
+static int vdp_bld_tlv(struct vdp_data *vd, struct vsi_profile *profile)
+{
+ int rc = 0;
+
+ if (!port_find_by_name(vd->ifname)) {
+ rc = EEXIST;
+ goto out_err;
+ }
+
+ if (!init_cfg()) {
+ rc = ENOENT;
+ goto out_err;
+ }
+
+ if (vdp_bld_vsi_tlv(vd, profile)) {
+ fprintf(stderr, "### %s:%s:vdp_bld_cfg_tlv() failed\n",
+ __func__, vd->ifname);
+ rc = EINVAL;
+ goto out_err_destroy;
+ }
+
+out_err_destroy:
+ destroy_cfg();
+
+out_err:
+ return rc;
+}
+
+/* vdp_gettlv - get the tlv for a profile
+ * @port: the port on which the tlv was received
+ * @profile: profile the vsi tlv is created from
+ *
+ * returns 0 on success
+ *
+ * this is the interface function called from ecp_build_ECPDU. It returns the
+ * packed tlv for a profile.
+ */
+struct packed_tlv *vdp_gettlv(struct port *port, struct vsi_profile *profile)
+{
+ int size;
+ struct vdp_data *vd;
+ struct packed_tlv *ptlv = NULL;
+
+ vd = vdp_data(port->ifname);
+ if (!vd) {
+ printf("%s(%i): Could not find vdp_data for %s !\n", __func__, __LINE__,
+ port->ifname);
+ goto out_err;
+ }
+
+ /* frees the unpacked_tlv in vdp_data
+ * also done in vdp_bld_vsi_tlv */
+ vdp_free_tlv(vd);
+
+ if (vdp_bld_tlv(vd, profile)) {
+ fprintf(stderr, "### %s:%s vdp_bld_tlv failed\n",
+ __func__, port->ifname);
+ goto out_err;
+ }
+
+ size = TLVSIZE(vd->vdp);
+
+ if (!size) {
+ printf("%s(%i): size %i of unpacked_tlv not correct !\n", __func__, __LINE__,
+ size);
+ goto out_err;
+ }
+
+ ptlv = create_ptlv();
+ if (!ptlv)
+ goto out_err;
+
+ ptlv->tlv = malloc(size);
+ if (!ptlv->tlv)
+ goto out_free;
+
+ ptlv->size = 0;
+ PACK_TLV_AFTER(vd->vdp, ptlv, size, out_free);
+
+ return ptlv;
+
+out_free:
+ ptlv = free_pkd_tlv(ptlv);
+out_err:
+ fprintf(stderr,"### %s:%s: failed\n", __func__, port->ifname);
+ return NULL;
+}
+
+/* vdp_profile_equal - checks for equality of 2 profiles
+ * @p1: profile 1
+ * @p2: profile 2
+ *
+ * returns 1 on success, 0 on error
+ *
+ * compares mgrid, id, version, instance, mac and vlan of 2 profiles to find
+ * out if they are equal.
+ */
+int vdp_profile_equal(struct vsi_profile *p1, struct vsi_profile *p2)
+{
+ if (p1->mgrid != p2->mgrid)
+ return 0;
+
+ if (memcmp(p1->id, p2->id, 3))
+ return 0;
+
+ if (p1->version != p2->version)
+ return 0;
+
+ if (memcmp(p1->instance, p2->instance, 16))
+ return 0;
+
+ if (memcmp(p1->mac, p2->mac, MAC_ADDR_LEN))
+ return 0;
+
+ if (p1->vlan != p2->vlan)
+ return 0;
+
+ return 1;
+}
+
+/* vdp_add_profile - adds a profile to a per port list
+ * @profile: profile to add
+ *
+ * returns the profile that has been found or added
+ *
+ * main interface function which adds a profile to a list kept on a per-port
+ * basis. Checks if the profile is already in the list, adds it if necessary.
+ */
+struct vsi_profile *vdp_add_profile(struct vsi_profile *profile)
+{
+ struct port *port;
+ struct vsi_profile *p;
+ struct vdp_data *vd;
+
+ vd = vdp_data(profile->port->ifname);
+ if (!vd) {
+ printf("%s(%i): Could not find vdp_data for %i !\n", __func__, __LINE__,
+ profile->port->ifname);
+ return NULL;
+ }
+
+ vdp_print_profile(profile);
+
+ /* loop over all existing profiles and check wether
+ * one for this combination already exists. If yes, check,
+ * if the MAC/VLAN pair already exists. If not, add it.
+ * Note: currently only one MAC/VLAN pair supported ! */
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ if (p) {
+ vdp_print_profile(p);
+ if (vdp_profile_equal(p, profile)) {
+ if (p->mode == profile->mode) {
+ printf("%s(%i): profile already exists, ignoring !\n",
+ __func__, __LINE__);
+ return NULL;
+ } else {
+ printf("%s(%i): taking new mode !\n", __func__,
+ __LINE__);
+ p->mode = profile->mode;
+ return p;
+ }
+ }
+ }
+ }
+
+ LIST_INSERT_HEAD(&vd->profile_head, profile, profile );
+
+ return profile;
+}
+
+/* vdp_ifdown - tear down vdp structures for a interface
+ * @ifname: name of the interface
+ *
+ * no return value
+ *
+ * interface function to lldpad. tears down vdp specific structures if
+ * interface "ifname" goes down.
+ */
+void vdp_ifdown(char *ifname)
+{
+ struct vdp_data *vd;
+ struct vsi_tlv *vtlv;
+ struct vsi_profile *p;
+
+ vd = vdp_data(ifname);
+ if (!vd)
+ goto out_err;
+
+ LIST_REMOVE(vd, entry);
+
+ LIST_FOREACH(p, &vd->profile_head, profile) {
+ free(p);
+ LIST_REMOVE(p, profile);
+ }
+ vdp_free_tlv(vd);
+ free(vd);
+ fprintf(stderr, "### %s:port %s removed\n", __func__, ifname);
+ return;
+out_err:
+ fprintf(stderr, "### %s:port %s adding failed\n", __func__, ifname);
+
+ return;
+}
+
+/* vdp_ifup - build up vdp structures for a interface
+ * @ifname: name of the interface
+ *
+ * no return value
+ *
+ * interface function to lldpad. builds up vdp specific structures if
+ * interface "ifname" goes down.
+ */
+void vdp_ifup(char *ifname)
+{
+ struct vdp_data *vd;
+ struct vdp_user_data *ud;
+ char arg_path[VDP_BUF_SIZE];
+
+ vd = vdp_data(ifname);
+ if (vd) {
+ fprintf(stderr, "### %s:%s exists\n", __func__, ifname);
+ goto out_err;
+ }
+
+ /* not found, alloc/init per-port module data */
+ vd = (struct vdp_data *) calloc(1, sizeof(struct vdp_data));
+ if (!vd) {
+ fprintf(stderr, "### %s:%s malloc %ld failed\n",
+ __func__, ifname, sizeof(*vd));
+ goto out_err;
+ }
+ strncpy(vd->ifname, ifname, IFNAMSIZ);
+
+ if (!init_cfg())
+ goto out_err;
+
+ if (get_config_setting(ifname, ARG_VDP_ROLE,
+ (void *)&vd->role, CONFIG_TYPE_INT)) {
+ vd->role = VDP_ROLE_STATION;
+ }
+
+ if (vd->role == VDP_ROLE_BRIDGE)
+ printf("### %s: configured for bridge mode (%i)!\n", ifname, vd->role);
+ else
+ printf("### %s: configured for station mode (%i)!\n", ifname, vd->role);
+
+ LIST_INIT(&vd->profile_head);
+
+ ud = find_module_user_data_by_if(ifname, &lldp_head, LLDP_MOD_VDP);
+ LIST_INSERT_HEAD(&ud->head, vd, entry);
+
+ ecp_init(ifname);
+
+ fprintf(stderr, "### %s:port %s added\n", __func__, ifname);
+ return;
+
+out_err:
+ fprintf(stderr, "### %s:port %s adding failed\n", __func__, ifname);
+ return;
+}
+
+static const struct lldp_mod_ops vdp_ops = {
+ .lldp_mod_register = vdp_register,
+ .lldp_mod_unregister = vdp_unregister,
+ .lldp_mod_ifup = vdp_ifup,
+ .lldp_mod_ifdown = vdp_ifdown,
+ .get_arg_handler = vdp_get_arg_handlers,
+};
+
+/* vdp_register - register vdp module to lldpad
+ * @none
+ *
+ * returns lldp_module struct on success, NULL on error
+ *
+ * allocates a module structure with vdp module information and returns it
+ * to lldpad.
+ */
+struct lldp_module *vdp_register(void)
+{
+ struct lldp_module *mod;
+ struct vdp_user_data *ud;
+
+ mod = malloc(sizeof(*mod));
+ if (!mod) {
+ fprintf(stderr, "failed to malloc module data\n");
+ log_message(MSG_ERR_SERVICE_START_FAILURE,
+ "%s", "failed to malloc module data");
+ goto out_err;
+ }
+ ud = malloc(sizeof(struct vdp_user_data));
+ if (!ud) {
+ free(mod);
+ fprintf(stderr, "failed to malloc module user data\n");
+ log_message(MSG_ERR_SERVICE_START_FAILURE,
+ "%s", "failed to malloc module user data");
+ goto out_err;
+ }
+ LIST_INIT(&ud->head);
+ mod->id = LLDP_MOD_VDP;
+ mod->ops = &vdp_ops;
+ mod->data = ud;
+ fprintf(stderr, "### %s:done\n", __func__);
+ return mod;
+
+out_err:
+ fprintf(stderr, "### %s:failed\n", __func__);
+ return NULL;
+}
+
+/* vdp_unregister - unregister vdp module from lldpad
+ * @none
+ *
+ * no return value
+ *
+ * frees vdp module structure.
+ */
+void vdp_unregister(struct lldp_module *mod)
+{
+ if (mod->data) {
+ vdp_free_data((struct vdp_user_data *) mod->data);
+ free(mod->data);
+ }
+ free(mod);
+ fprintf(stderr, "### %s:done\n", __func__);
+}
+
+
diff --git a/lldpad.c b/lldpad.c
index 711a995..7a8b75b 100644
--- a/lldpad.c
+++ b/lldpad.c
@@ -50,6 +50,7 @@
#include "lldp_med.h"
#include "lldp_8023.h"
#include "lldp_evb.h"
+#include "lldp_vdp.h"
#include "config.h"
#include "lldpad_shm.h"
#include "clif.h"
@@ -65,6 +66,7 @@ struct lldp_module *(*register_tlv_table[])(void) = {
med_register,
ieee8023_register,
evb_register,
+ vdp_register,
NULL,
};
--
1.7.1
^ permalink raw reply related
* [PATCH 1/3] ECP implementation
From: Jens Osterkamp @ 2010-06-17 13:57 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw, Jens Osterkamp
In-Reply-To: <1276783031-10239-1-git-send-email-jens@linux.vnet.ibm.com>
This is the implementation of the edge control protocol (ECP) as specified
in IEEE 802.1Qbg.
For this it extends the infrastructure defined lldpad to send and receive
ECP frames with a new (yet to be defined) ethertype.
Received frames are validated and analyzed before the content is handed to the
upper layer protocol (ULP, VDP in this case) for further processing. Frames
to be transmitted are compiled from VSI (guest interface) profiles registered
on a interface.
Reception and transmission of ECP frames is controlled by RX and TX state
machines, timeouts are handled timeout functions.
The patch still contains a lot of debug code to allow low-level protocol
analysis.
Signed-off-by: Jens Osterkamp <jens@linux.vnet.ibm.com>
---
Makefile.am | 2 +
ecp/ecp.c | 77 +++++++
ecp/ecp.h | 92 ++++++++
ecp/ecp_rx.c | 597 ++++++++++++++++++++++++++++++++++++++++++++++++++++
ecp/ecp_tx.c | 467 ++++++++++++++++++++++++++++++++++++++++
include/lldp_evb.h | 6 +
include/lldp_vdp.h | 157 ++++++++++++++
lldp/l2_packet.h | 2 +
lldp/ports.h | 25 ++-
lldp_evb.c | 2 +
10 files changed, 1424 insertions(+), 3 deletions(-)
create mode 100644 ecp/ecp.c
create mode 100644 ecp/ecp.h
create mode 100644 ecp/ecp_rx.c
create mode 100644 ecp/ecp_tx.c
create mode 100644 include/lldp_vdp.h
diff --git a/Makefile.am b/Makefile.am
index 554baec..693e57d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -58,6 +58,8 @@ $(lldpad_include_HEADERS) $(noinst_HEADERS) \
lldp/ports.c lldp/agent.c lldp/l2_packet_linux.c lldp/tx.c \
lldp/rx.c lldp/agent.h lldp/l2_packet.h lldp/mibdata.h lldp/ports.h \
lldp/states.h \
+ecp/ecp.c ecp/ecp_tx.c \
+ecp/ecp_rx.c \
include/lldp.h include/lldp_mod.h \
lldp_dcbx.c include/lldp_dcbx.h tlv_dcbx.c include/tlv_dcbx.h \
lldp_dcbx_cfg.c include/lldp_dcbx_cfg.h \
diff --git a/ecp/ecp.c b/ecp/ecp.c
new file mode 100644
index 0000000..ecf68f9
--- /dev/null
+++ b/ecp/ecp.c
@@ -0,0 +1,77 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include <net/if.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <linux/if_bridge.h>
+#include "lldp.h"
+#include "lldp_evb.h"
+#include "messages.h"
+#include "config.h"
+#include "common.h"
+#include "lldp/l2_packet.h"
+#include "lldp/ports.h"
+#include "ecp/ecp.h"
+
+/* ecp_init - initialize ecp module
+ * @ifname: interface for which the module is initialized
+ *
+ * returns 0 on success, -1 on error
+ *
+ * finds the port to the interface name, sets up the receive handle for
+ * incoming ecp frames and initializes the ecp rx and tx state machines.
+ * should usually be called when a successful exchange of EVB TLVs has been
+ * made and ECP and VDP protocols are supported by both sides.
+ */
+int ecp_init(char *ifname)
+{
+ struct port *port;
+
+ port = port_find_by_name(ifname);
+
+ if (!port) {
+ printf("%s(%i): unable to find port %s ! \n", __func__, __LINE__, ifname);
+ goto fail;
+ }
+
+ port->ecp.l2 = l2_packet_init(port->ifname, NULL, ETH_P_ECP,
+ ecp_rx_ReceiveFrame, port, 1);
+ if (!port->ecp.l2) {
+ printf("ERROR: Failed to open register layer 2 access to "
+ "ETH_P_ECP\n");
+ goto fail;
+ }
+
+ ecp_tx_run_sm(port);
+ ecp_rx_run_sm(port);
+
+ return 0;
+
+fail:
+ return -1;
+}
diff --git a/ecp/ecp.h b/ecp/ecp.h
new file mode 100644
index 0000000..d08f873
--- /dev/null
+++ b/ecp/ecp.h
@@ -0,0 +1,92 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _ECP_H
+#define _ECP_H
+
+#include "lldp/ports.h"
+#include "lldp_mod.h"
+
+#define ECP_SUBTYPE 0x0
+
+#define ECP_MAX_RETRIES 3
+#define ECP_SEQUENCE_NR_START 0x0
+
+#define ECP_TRANSMISSION_TIMER EVB_RTM(RTE)*EVB_RTG
+#define ECP_TRANSMISSION_DIVIDER 10000
+
+typedef enum {
+ ECP_REQUEST = 0,
+ ECP_ACK
+} ecp_mode;
+
+struct ecp_hdr {
+ u8 oui[3];
+ u8 pad1;
+ u16 subtype;
+ u8 mode;
+ u16 seqnr;
+} __attribute__ ((__packed__));
+
+enum {
+ ECP_TX_IDLE,
+ ECP_TX_INIT_TRANSMIT,
+ ECP_TX_TRANSMIT_ECPDU,
+ ECP_TX_WAIT_FOR_ACK,
+ ECP_TX_REQUEST_PDU
+};
+
+static const char *ecp_tx_states[] = {
+ "ECP_TX_IDLE",
+ "ECP_TX_INIT_TRANSMIT",
+ "ECP_TX_TRANSMIT_ECPDU",
+ "ECP_TX_WAIT_FOR_ACK",
+ "ECP_TX_REQUEST_PDU"
+};
+
+void ecp_tx_run_sm(struct port *);
+
+enum {
+ ECP_RX_IDLE,
+ ECP_RX_INIT_RECEIVE,
+ ECP_RX_RECEIVE_WAIT,
+ ECP_RX_RECEIVE_ECPDU,
+ ECP_RX_SEND_ACK,
+ ECP_RX_RESEND_ACK,
+};
+
+static const char *ecp_rx_states[] = {
+ "ECP_RX_IDLE",
+ "ECP_RX_INIT_RECEIVE",
+ "ECP_RX_RECEIVE_WAIT",
+ "ECP_RX_RECEIVE_ECPDU",
+ "ECP_RX_SEND_ACK",
+ "ECP_RX_RESEND_ACK",
+};
+
+void ecp_rx_ReceiveFrame(void *, unsigned int, const u8 *, size_t );
+void ecp_rx_run_sm(struct port *);
+
+#endif /* _ECP_H */
diff --git a/ecp/ecp_rx.c b/ecp/ecp_rx.c
new file mode 100644
index 0000000..d8c050f
--- /dev/null
+++ b/ecp/ecp_rx.c
@@ -0,0 +1,597 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "dcb_osdep.h"
+#include "lldp/ports.h"
+#include "lldp/l2_packet.h"
+#include "messages.h"
+#include "ecp.h"
+#include "lldp.h"
+#include "lldpad.h"
+#include "lldp_mod.h"
+#include "clif_msgs.h"
+#include "lldp_mand.h"
+
+/* ecp_rx_Initialize - initializes the ecp rx state machine
+ * @port: port for the state machine
+ *
+ * no return value
+ *
+ * initialize some variables, get rid of old frame if necessary
+ */
+void ecp_rx_Initialize(struct port *port)
+{
+ port->ecp.rx.rcvFrame = false;
+ port->ecp.rx.badFrame = false;
+
+ port->ecp.ackReceived = 0;
+
+ if (port->rx.framein) {
+ free(port->rx.framein);
+ port->rx.framein = NULL;
+ }
+ port->rx.sizein = 0;
+
+ return;
+}
+
+/* ecp_rx_freeFrame - free up received frame
+ * @port: port for the state machine
+ *
+ * no return value
+ *
+ * frees up an old received frame, set pointer to NULL and size to 0.
+ */
+void ecp_rx_freeFrame(struct port *port)
+{
+ free(port->ecp.rx.framein);
+ port->ecp.rx.framein = NULL;
+ port->ecp.rx.sizein = 0;
+}
+
+/* ecp_print_framein - print raw received frame
+ * @port: port for the state machine
+ *
+ * no return value
+ *
+ * prints out a raw version of a received frame. useful for low-level protocol
+ * debugging.
+ */
+void ecp_print_framein(struct port *port)
+{
+ int i;
+
+ for (i=0; i < port->ecp.rx.sizein; i++) {
+ printf("%02x ", port->ecp.rx.framein[i]);
+ if (!((i+1) % 16))
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+/* ecp_rx_send_ack_frame - send out ack frame for received frame
+ * @port: port for the state machine
+ *
+ * no return value
+ *
+ * creates an ack frame for a just received frame, prints the about to be
+ * sent frame and finally transmits it.
+ */
+void ecp_rx_send_ack_frame(struct port *port)
+{
+ ecp_rx_SendAckFrame(port);
+
+ ecp_print_frameout(port);
+
+ ecp_txFrame(port);
+
+ return;
+}
+
+/* ecp_rx_ReceiveFrame - receive ecp frame
+ * @ctx: rx callback context, struct port * in this case
+ * @ifindex: index of interface
+ * @buf: buffer which contains the frame just received
+ * @len: size of buffer (frame)
+ *
+ * no return value
+ *
+ * creates a local copy of the buffer and checks the header. keeps some
+ * statistics about ecp frames. Checks if it is a request or an ack frame and branches
+ * to ecp rx or ecp tx state machine.
+ */
+void ecp_rx_ReceiveFrame(void *ctx, unsigned int ifindex, const u8 *buf, size_t len)
+{
+
+ struct port * port;
+ u8 frame_error = 0;
+ u8 own_addr[ETH_ALEN];
+ u16 tlv_offset;
+ struct l2_ethhdr *hdr;
+ struct l2_ethhdr example_hdr,*ex;
+ struct ecp_hdr *ecp_hdr;
+ char msg[2] = "";
+
+ if (ctx)
+ port = (struct port *)ctx;
+
+ printf("%s(%i)-%s: received packet with size %i\n", __func__, __LINE__,
+ port->ifname, len);
+
+ if (port->adminStatus == disabled || port->adminStatus == enabledTxOnly)
+ return;
+
+ if (port->ecp.rx.framein &&
+ port->ecp.rx.sizein == len &&
+ (memcmp(buf, port->ecp.rx.framein, len) == 0)) {
+ port->ecp.stats.statsFramesInTotal++;
+ return;
+ }
+
+ if (port->ecp.rx.framein)
+ free(port->ecp.rx.framein);
+
+ port->ecp.rx.framein = (u8 *)malloc(len);
+ if (port->ecp.rx.framein == NULL) {
+ printf("ERROR - could not allocate memory for rx'ed frame\n");
+ return;
+ }
+ memcpy(port->ecp.rx.framein, buf, len);
+
+ port->ecp.rx.sizein = (u16)len;
+ ex = &example_hdr;
+ memcpy(ex->h_dest, multi_cast_source, ETH_ALEN);
+ ex->h_proto = htons(ETH_P_ECP);
+ hdr = (struct l2_ethhdr *)port->ecp.rx.framein;
+
+ if ((memcmp(hdr->h_dest,ex->h_dest, ETH_ALEN) != 0)) {
+ printf("ERROR multicast address error in incoming frame. "
+ "Dropping frame.\n");
+ frame_error++;
+ free(port->ecp.rx.framein);
+ port->ecp.rx.framein = NULL;
+ port->ecp.rx.sizein = 0;
+ return;
+ }
+
+ if (hdr->h_proto != example_hdr.h_proto) {
+ printf("ERROR Ethertype not ECP ethertype but ethertype "
+ "'%x' in incoming frame.\n", htons(hdr->h_proto));
+ frame_error++;
+ free(port->ecp.rx.framein);
+ port->ecp.rx.framein = NULL;
+ port->ecp.rx.sizein = 0;
+ return;
+ }
+
+ if (!frame_error) {
+ port->ecp.stats.statsFramesInTotal++;
+ port->ecp.rx.rcvFrame = 1;
+ }
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_hdr = (struct ecp_hdr *)&port->ecp.rx.framein[tlv_offset];
+
+ port->ecp.seqECPDU = ecp_hdr->seqnr;
+
+ switch(ecp_hdr->mode) {
+ case ECP_REQUEST:
+ port->ecp.ackReceived = false;
+ ecp_rx_run_sm(port);
+ break;
+ case ECP_ACK:
+ port->ecp.ackReceived = true;
+ printf("%s(%i)-%s: received ack frame \n", __func__, __LINE__, port->ifname);
+ ecp_print_framein(port);
+ ecp_tx_run_sm(port);
+ port->ecp.ackReceived = false;
+ break;
+ default:
+ printf("ERROR: unknown mode %i!\n", ecp_hdr->mode);
+ return;
+ }
+
+ ecp_rx_freeFrame(port);
+}
+
+/* ecp_rx_validateFrame - validates received frame
+ * @port: port used by ecp
+ *
+ * no return value
+ *
+ * checks wether received frame has correct subtype and mode
+ */
+
+void ecp_rx_validateFrame(struct port * port)
+{
+ u16 tlv_offset = 0;
+ struct ecp_hdr *ecp_hdr;
+
+ printf("%s(%i)-%s: validating frame \n", __func__, __LINE__, port->ifname);
+
+ assert(port->ecp.rx.framein && port->ecp.rx.sizein);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_hdr = (struct ecp_hdr *)&port->ecp.rx.framein[tlv_offset];
+
+ printf("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
+ __func__, __LINE__, port->ifname, ecp_hdr->subtype, ecp_hdr->mode, ecp_hdr->seqnr);
+
+ if (ecp_hdr->subtype != ECP_SUBTYPE) {
+ printf("ERROR: unknown subtype !\n");
+ return;
+ }
+
+ if ((ecp_hdr->oui[0] != 0x0) || (ecp_hdr->oui[1] != 0x1b) ||
+ (ecp_hdr->oui[2] != 0x3f)) {
+ printf("ERROR: incorrect OUI 0x%02x%02x%02x !\n",
+ ecp_hdr->oui[0], ecp_hdr->oui[1], ecp_hdr->oui[2]);
+ return;
+ }
+
+ switch(ecp_hdr->mode) {
+ case ECP_REQUEST:
+ break;
+ case ECP_ACK:
+ break;
+ default:
+ printf("ERROR: unknown mode %i!\n", ecp_hdr->mode);
+ return;
+ }
+
+ /* FIXME: also done in ecp_rx_ReceiveFrame,
+ * are both necessary ? */
+ port->ecp.seqECPDU = ecp_hdr->seqnr;
+}
+
+/* ecp_rx_SendAckFrame - send ack frame
+ * @port: port used by ecp
+ *
+ * currently always returns 0
+ *
+ * copies current received frame over to frame out, fills in address of this
+ * port and set mode field to ACK. used by ecp_rx_send_ack_frame.
+ */
+int ecp_rx_SendAckFrame(struct port * port)
+{
+ u16 tlv_offset = 0;
+ struct ecp_hdr *ecp_hdr;
+ struct l2_ethhdr *hdr;
+ u8 own_addr[ETH_ALEN];
+
+ printf("%s(%i)-%s: acking frame \n", __func__, __LINE__, port->ifname);
+
+ assert(port->ecp.rx.framein && port->ecp.rx.sizein);
+
+ /* copy over to frameout */
+ port->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
+ memcpy(port->ecp.tx.frameout, port->ecp.rx.framein, port->ecp.rx.sizein);
+ port->ecp.tx.sizeout = port->ecp.rx.sizein;
+
+ /* use my own addr to send ACK */
+ hdr = (struct l2_ethhdr *)port->ecp.tx.frameout;
+ l2_packet_get_own_src_addr(port->ecp.l2,(u8 *)&own_addr);
+ memcpy(hdr->h_source, &own_addr, ETH_ALEN);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+ ecp_hdr = (struct ecp_hdr *)&port->ecp.tx.frameout[tlv_offset];
+ ecp_hdr->mode = ECP_ACK;
+
+ return 0;
+}
+
+/* ecp_rx_validate_frame - wrapper around ecp_rx_validateFrame
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * sets rcvFrame to false and validates frame. used in ECP_RX_RECEIVE_ECPDU
+ * state of ecp_rx_run_sm
+ */
+void ecp_rx_validate_frame(struct port *port)
+{
+ port->ecp.rx.rcvFrame = false;
+ ecp_rx_validateFrame(port);
+ return;
+}
+
+/* ecp_rx_ProcessFrame - process received ecp frames
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * walks through the packed vsi tlvs in an ecp frame, extracts them
+ * and passes them to the VDP ULP with vdp_indicate.
+ */
+void ecp_rx_ProcessFrame(struct port * port)
+{
+ u16 tlv_cnt = 0;
+ u8 tlv_type = 0;
+ u16 tlv_length = 0;
+ u16 tlv_offset = 0;
+ u16 *tlv_head_ptr = NULL;
+ u8 frame_error = 0;
+ bool tlv_stored = false;
+ int err;
+ struct lldp_module *np;
+ struct ecp_hdr *ecp_hdr;
+
+ printf("%s(%i)-%s: processing frame \n", __func__, __LINE__, port->ifname);
+
+ assert(port->ecp.rx.framein && port->ecp.rx.sizein);
+
+ tlv_offset = sizeof(struct l2_ethhdr);
+
+ ecp_print_framein(port);
+
+ ecp_hdr = (struct ecp_hdr *)&port->ecp.rx.framein[tlv_offset];
+
+ printf("%s(%i)-%s: ecp packet with subtype %04x, mode %02x, sequence nr %04x\n",
+ __func__, __LINE__, port->ifname, ecp_hdr->subtype, ecp_hdr->mode, ecp_hdr->seqnr);
+
+ /* FIXME: already done in ecp_rx_validateFrame ? */
+ if (ecp_hdr->subtype != ECP_SUBTYPE) {
+ printf("ERROR: unknown subtype !\n");
+ frame_error++;
+ goto out;
+ }
+
+ /* processing of VSI_TLVs starts here */
+
+ tlv_offset += sizeof(struct ecp_hdr);
+
+ do {
+ tlv_cnt++;
+ if (tlv_offset > port->ecp.rx.sizein) {
+ printf("%s(%i)-%s: ERROR: Frame overrun! tlv_offset %i, sizein %i cnt %i\n",
+ __func__, __LINE__, port->ifname, tlv_offset, port->ecp.rx.sizein, tlv_cnt);
+ frame_error++;
+ goto out;
+ }
+
+ tlv_head_ptr = (u16 *)&port->ecp.rx.framein[tlv_offset];
+ tlv_length = htons(*tlv_head_ptr) & 0x01FF;
+ tlv_type = (u8)(htons(*tlv_head_ptr) >> 9);
+
+ u16 tmp_offset = tlv_offset + tlv_length;
+ if (tmp_offset > port->ecp.rx.sizein) {
+ printf("ERROR: Frame overflow error: offset=%d, "
+ "rx.size=%d \n", tmp_offset, port->ecp.rx.sizein);
+ frame_error++;
+ goto out;
+ }
+
+ u8 *info = (u8 *)&port->ecp.rx.framein[tlv_offset +
+ sizeof(*tlv_head_ptr)];
+
+ struct unpacked_tlv *tlv = create_tlv();
+
+ if (!tlv) {
+ printf("ERROR: Failed to malloc space for "
+ "incoming TLV. \n");
+ goto out;
+ }
+
+ if ((tlv_length == 0) && (tlv->type != TYPE_0)) {
+ printf("ERROR: tlv_length == 0\n");
+ free_unpkd_tlv(tlv);
+ goto out;
+ }
+
+ tlv->type = tlv_type;
+ tlv->length = tlv_length;
+ tlv->info = (u8 *)malloc(tlv_length);
+ if (tlv->info) {
+ memset(tlv->info,0, tlv_length);
+ memcpy(tlv->info, info, tlv_length);
+ } else {
+ printf("ERROR: Failed to malloc space for incoming "
+ "TLV info \n");
+ free_unpkd_tlv(tlv);
+ goto out;
+ }
+
+ /* Validate the TLV */
+ tlv_offset += sizeof(*tlv_head_ptr) + tlv_length;
+
+ if (tlv->type == TYPE_127) { /* private TLV */
+ /* TODO: give VSI TLV to VDP */
+ }
+
+ if ((tlv->type != TYPE_0) && !tlv_stored) {
+ printf("\n%s: allocated TLV (%lu) "
+ " was not stored! (%p)\n", __func__, tlv->type,
+ tlv);
+ tlv = free_unpkd_tlv(tlv);
+ port->ecp.stats.statsTLVsUnrecognizedTotal++;
+ }
+
+ tlv = NULL;
+ tlv_stored = false;
+ } while(tlv_type != 0);
+
+out:
+ if (frame_error) {
+ port->ecp.stats.statsFramesDiscardedTotal++;
+ port->ecp.stats.statsFramesInErrorsTotal++;
+ port->ecp.rx.badFrame = true;
+ }
+
+ return;
+}
+
+/* ecp_rx_change_state - changes the ecp rx sm state
+ * @port: currently used port
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * checks state transistion for consistency and finally changes the state of
+ * the profile.
+ */
+void ecp_rx_change_state(struct port *port, u8 newstate)
+{
+ switch(newstate) {
+ case ECP_RX_IDLE:
+ break;
+ case ECP_RX_INIT_RECEIVE:
+ break;
+ case ECP_RX_RECEIVE_WAIT:
+ assert((port->ecp.rx.state == ECP_RX_INIT_RECEIVE) ||
+ (port->ecp.rx.state == ECP_RX_IDLE) ||
+ (port->ecp.rx.state == ECP_RX_SEND_ACK) ||
+ (port->ecp.rx.state == ECP_RX_RESEND_ACK));
+ break;
+ case ECP_RX_RECEIVE_ECPDU:
+ assert(port->ecp.rx.state == ECP_RX_RECEIVE_WAIT);
+ break;
+ case ECP_RX_SEND_ACK:
+ assert(port->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
+ break;
+ case ECP_RX_RESEND_ACK:
+ assert(port->ecp.rx.state == ECP_RX_RECEIVE_ECPDU);
+ break;
+ default:
+ printf("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "%s", port->ifname);
+ }
+
+ printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ port->ifname, ecp_rx_states[port->ecp.rx.state], ecp_rx_states[newstate]);
+
+ port->ecp.rx.state = newstate;
+}
+
+/* ecp_set_rx_state - sets the ecp rx sm state
+ * @port: currently used port
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+bool ecp_set_rx_state(struct port *port)
+{
+ if (port->portEnabled == false) {
+ ecp_rx_change_state(port, ECP_RX_IDLE);
+ }
+
+ switch(port->ecp.rx.state) {
+ case ECP_RX_IDLE:
+ if (port->portEnabled == true) {
+ ecp_rx_change_state(port, ECP_RX_INIT_RECEIVE);
+ return true;
+ }
+ return false;
+ case ECP_RX_INIT_RECEIVE:
+ if ((port->adminStatus == enabledRxTx) ||
+ (port->adminStatus == enabledRxOnly)) {
+ ecp_rx_change_state(port, ECP_RX_RECEIVE_WAIT);
+ return true;
+ }
+ return false;
+ case ECP_RX_RECEIVE_WAIT:
+ if ((port->adminStatus == disabled) ||
+ (port->adminStatus == enabledTxOnly)) {
+ ecp_rx_change_state(port, ECP_RX_IDLE);
+ return true;
+ }
+ if (port->ecp.rx.rcvFrame == true) {
+ ecp_rx_change_state(port, ECP_RX_RECEIVE_ECPDU);
+ return true;
+ }
+ return false;
+ case ECP_RX_RECEIVE_ECPDU:
+ if (port->ecp.seqECPDU == port->ecp.lastSequence) {
+ printf("%s(%i):-(%s) seqECPDU %x, lastSequence %x\n", __func__, __LINE__,
+ port->ifname, port->ecp.seqECPDU, port->ecp.lastSequence);
+ ecp_rx_change_state(port, ECP_RX_RESEND_ACK);
+ return true;
+ }
+ if (port->ecp.seqECPDU != port->ecp.lastSequence) {
+ ecp_rx_change_state(port, ECP_RX_SEND_ACK);
+ return true;
+ }
+ return false;
+ case ECP_RX_SEND_ACK:
+ ecp_rx_change_state(port, ECP_RX_RECEIVE_WAIT);
+ return false;
+ case ECP_RX_RESEND_ACK:
+ ecp_rx_change_state(port, ECP_RX_RECEIVE_WAIT);
+ return false;
+ default:
+ printf("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_RX_SM_INVALID, "%s", port->ifname);
+ return false;
+ }
+}
+
+/* ecp_rx_run_sm - state machine for ecp rx
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * runs the state machine for ecp rx.
+ */
+void ecp_rx_run_sm(struct port *port)
+{
+ ecp_set_rx_state(port);
+ do {
+ printf("%s(%i)-%s: ecp_rx - %s\n", __func__, __LINE__,
+ port->ifname, ecp_rx_states[port->ecp.tx.state]);
+
+ switch(port->ecp.rx.state) {
+ case ECP_RX_IDLE:
+ break;
+ case ECP_RX_INIT_RECEIVE:
+ ecp_rx_Initialize(port);
+ break;
+ case ECP_RX_RECEIVE_WAIT:
+ break;
+ case ECP_RX_RECEIVE_ECPDU:
+ ecp_rx_validate_frame(port);
+ break;
+ case ECP_RX_SEND_ACK:
+ ecp_rx_ProcessFrame(port);
+ port->ecp.lastSequence = port->ecp.seqECPDU;
+ break;
+ case ECP_RX_RESEND_ACK:
+ ecp_rx_ProcessFrame(port);
+ if (!port->ecp.ackReceived) {
+ ecp_rx_send_ack_frame(port);
+ ecp_rx_freeFrame(port);
+ }
+ break;
+ default:
+ printf("ERROR: The ECP_RX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", port->ifname);
+ }
+ } while (ecp_set_rx_state(port) == true);
+
+}
diff --git a/ecp/ecp_tx.c b/ecp/ecp_tx.c
new file mode 100644
index 0000000..d31edba
--- /dev/null
+++ b/ecp/ecp_tx.c
@@ -0,0 +1,467 @@
+/*******************************************************************************
+
+ implementation of ECP according to 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#include "dcb_osdep.h"
+#include "lldp/ports.h"
+#include "lldp/l2_packet.h"
+#include "messages.h"
+#include "ecp.h"
+#include "lldpad.h"
+#include "lldp_tlv.h"
+#include "lldp_mod.h"
+#include "lldp_mand.h"
+#include "lldp_evb.h"
+#include "include/lldp_vdp.h"
+
+/* ecp_somethingChangedLocal - set flag if port has changed
+ * @port: port to set the flag for
+ * @mode: mode to set the flag to
+ *
+ * no return value
+ *
+ * set the localChange flag with a mode to indicate a port has changed.
+ * used to signal an ecpdu needs to be sent out.
+ */
+
+void ecp_somethingChangedLocal(struct port *port, int mode)
+{
+ if (!port)
+ return;
+
+ port->ecp.tx.localChange |= mode;
+
+ return;
+}
+
+/* ecp_print_frameout - print outbound frame
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * prints a raw dump of an outbound ecp frame. useful for low-level protocol
+ * debugging.
+ */
+void ecp_print_frameout(struct port *port)
+{
+ int i;
+
+ for (i=0; i < port->ecp.tx.sizeout; i++) {
+ printf("%02x ", port->ecp.tx.frameout[i]);
+ if (!((i+1) % 16))
+ printf("\n");
+ }
+
+ printf("\n");
+}
+
+/* ecp_build_ECPDU - create an ecp protocol data unit
+ * @port: currently used port
+ * @mode: mode to create pdu with (REQ or ACK)
+ *
+ * returns true on success, false on failure
+ *
+ * creates the frame header with the ports mac address, the ecp header with REQ
+ * or ACK mode, plus a list of packed TLVs created from the profiles on this
+ * port.
+ */
+bool ecp_build_ECPDU(struct port *port, int mode)
+{
+ struct l2_ethhdr eth;
+ struct ecp_hdr ecp_hdr;
+ u8 own_addr[ETH_ALEN];
+ u32 fb_offset = 0;
+ u32 datasize = 0;
+ struct packed_tlv *ptlv = NULL;
+ struct lldp_module *np;
+ struct vdp_data *vd;
+ struct vsi_profile *p;
+
+ if (port->ecp.tx.frameout) {
+ free(port->ecp.tx.frameout);
+ port->ecp.tx.frameout = NULL;
+ }
+
+ /* TODO: different multicast address for sending ECP over S-channel (multi_cast_source_s)
+ * S-channels to implement later */
+ memcpy(eth.h_dest, multi_cast_source, ETH_ALEN);
+ l2_packet_get_own_src_addr(port->ecp.l2,(u8 *)&own_addr);
+ memcpy(eth.h_source, &own_addr, ETH_ALEN);
+ eth.h_proto = htons(ETH_P_ECP);
+ port->ecp.tx.frameout = (u8 *)malloc(ETH_FRAME_LEN);
+ if (port->ecp.tx.frameout == NULL) {
+ printf("InfoECPDU: Failed to malloc frame buffer \n");
+ return false;
+ }
+ memset(port->ecp.tx.frameout,0,ETH_FRAME_LEN);
+ memcpy(port->ecp.tx.frameout, (void *)ð, sizeof(struct l2_ethhdr));
+ fb_offset += sizeof(struct l2_ethhdr);
+
+ ecp_hdr.oui[0] = 0x0;
+ ecp_hdr.oui[1] = 0x1b;
+ ecp_hdr.oui[2] = 0x3f;
+
+ ecp_hdr.pad1 = 0x0;
+
+ ecp_hdr.subtype = ECP_SUBTYPE;
+ switch(mode) {
+ case VDP_PROFILE_REQ:
+ ecp_hdr.mode = ECP_REQUEST;
+ break;
+ case VDP_PROFILE_ACK:
+ ecp_hdr.mode = ECP_ACK;
+ break;
+ default:
+ printf("%s(%i): unknown mode for %s !\n", __func__, __LINE__,
+ port->ifname);
+ }
+
+ ecp_hdr.seqnr = port->ecp.lastSequence;
+
+ if ((sizeof(struct ecp_hdr)+fb_offset) > ETH_MAX_DATA_LEN)
+ goto error;
+ memcpy(port->ecp.tx.frameout+fb_offset, (void *)&ecp_hdr, sizeof(struct ecp_hdr));
+ datasize += sizeof(struct ecp_hdr);
+ fb_offset += sizeof(struct ecp_hdr);
+
+ /* TODO: create tlvs from profiles here */
+
+ /* The End TLV marks the end of the LLDP PDU */
+ ptlv = pack_end_tlv();
+ if (!ptlv || ((ptlv->size + fb_offset) > ETH_MAX_DATA_LEN))
+ goto error;
+ memcpy(port->ecp.tx.frameout + fb_offset, ptlv->tlv, ptlv->size);
+ datasize += ptlv->size;
+ fb_offset += ptlv->size;
+ ptlv = free_pkd_tlv(ptlv);
+
+ if (datasize < ETH_MIN_DATA_LEN)
+ port->ecp.tx.sizeout = ETH_MIN_PKT_LEN;
+ else
+ port->ecp.tx.sizeout = fb_offset;
+
+ return true;
+
+error:
+ ptlv = free_pkd_tlv(ptlv);
+ if (port->ecp.tx.frameout)
+ free(port->ecp.tx.frameout);
+ port->ecp.tx.frameout = NULL;
+ printf("InfoECPDU: packed TLV too large for tx frame\n");
+ return false;
+}
+
+/* ecp_tx_Initialize - initializes the ecp tx state machine
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * initializes some variables for the ecp tx state machine.
+ */
+void ecp_tx_Initialize(struct port *port)
+{
+ if (port->ecp.tx.frameout) {
+ free(port->ecp.tx.frameout);
+ port->ecp.tx.frameout = NULL;
+ }
+ port->ecp.tx.localChange = VDP_PROFILE_REQ;
+ port->ecp.lastSequence = ECP_SEQUENCE_NR_START;
+ port->ecp.stats.statsFramesOutTotal = 0;
+ port->ecp.ackTimerExpired = false;
+ port->ecp.retries = 0;
+ l2_packet_get_port_state(port->ecp.l2, (u8 *)&(port->portEnabled));
+
+ return;
+}
+
+/* ecp_txFrame - transmit ecp frame
+ * @port: currently used port
+ *
+ * returns the number of characters sent on success, -1 on failure
+ *
+ * sends out the frame stored in the frameout structure using l2_packet_send.
+ */
+u8 ecp_txFrame(struct port *port)
+{
+ int status = 0;
+
+ status = l2_packet_send(port->ecp.l2, (u8 *)&multi_cast_source,
+ htons(ETH_P_ECP),port->ecp.tx.frameout,port->ecp.tx.sizeout);
+ port->ecp.stats.statsFramesOutTotal++;
+
+ return status;
+}
+
+/* ecp_tx_create_frame - create ecp frame
+ * @port: currently used port
+ *
+ * no return value
+ *
+ *
+ */
+void ecp_tx_create_frame(struct port *port)
+{
+ /* send REQs */
+ if (port->ecp.tx.localChange & VDP_PROFILE_REQ) {
+ printf("%s(%i)-%s: sending REQs\n", __func__, __LINE__, port->ifname);
+ ecp_build_ECPDU(port, VDP_PROFILE_REQ);
+ ecp_print_frameout(port);
+ ecp_txFrame(port);
+ }
+
+ /* send ACKs */
+ if (port->ecp.tx.localChange & VDP_PROFILE_ACK) {
+ printf("%s(%i)-%s: sending ACKs\n", __func__, __LINE__, port->ifname);
+ ecp_build_ECPDU(port, VDP_PROFILE_ACK);
+ ecp_print_frameout(port);
+ ecp_txFrame(port);
+ }
+
+ port->ecp.tx.localChange = 0;
+ return;
+}
+
+/* ecp_timeout_handler - handles the ack timer expiry
+ * @eloop_data: data structure of event loop
+ * @user_ctx: user context, port here
+ *
+ * no return value
+ *
+ * called when the ECP ack timer has expired. sets a flag and calls the ECP
+ * state machine.
+ */
+static void ecp_tx_timeout_handler(void *eloop_data, void *user_ctx)
+{
+ struct port *port;
+
+ port = (struct port *) user_ctx;
+
+ port->ecp.ackTimerExpired = true;
+
+ printf("%s(%i)-%s: timer expired\n", __func__, __LINE__,
+ port->ifname);
+
+ ecp_tx_run_sm(port);
+}
+
+/* ecp_tx_stop_ackTimer - stop the ECP ack timer
+ * @port: currently used port
+ *
+ * returns the number of removed handlers
+ *
+ * stops the ECP ack timer. used when a ack frame for the port has been
+ * received.
+ */
+static int ecp_tx_stop_ackTimer(struct port *port)
+{
+ printf("%s(%i)-%s: stopping timer\n", __func__, __LINE__,
+ port->ifname);
+
+ return eloop_cancel_timeout(ecp_tx_timeout_handler, NULL, (void *) port);
+}
+
+/* ecp_tx_start_ackTimer - starts the ECP ack timer
+ * @profile: profile to process
+ *
+ * returns 0 on success, -1 on error
+ *
+ * starts the ack timer when a frame has been sent out.
+ */
+static void ecp_tx_start_ackTimer(struct port *port)
+{
+ unsigned int secs, usecs;
+
+ port->ecp.ackTimerExpired = false;
+
+ secs = ECP_TRANSMISSION_TIMER / ECP_TRANSMISSION_DIVIDER;
+ usecs = ECP_TRANSMISSION_TIMER % ECP_TRANSMISSION_DIVIDER;
+
+ printf("%s(%i)-%s: starting timer\n", __func__, __LINE__,
+ port->ifname);
+
+ eloop_register_timeout(secs, usecs, ecp_tx_timeout_handler, NULL, (void *) port);
+}
+
+/* ecp_tx_change_state - changes the ecp tx sm state
+ * @port: currently used port
+ * @newstate: new state for the sm
+ *
+ * no return value
+ *
+ * checks state transistion for consistency and finally changes the state of
+ * the profile.
+ */
+static void ecp_tx_change_state(struct port *port, u8 newstate)
+{
+ switch(newstate) {
+ case ECP_TX_IDLE:
+ break;
+ case ECP_TX_INIT_TRANSMIT:
+ assert(port->ecp.tx.state == ECP_TX_IDLE);
+ break;
+ case ECP_TX_TRANSMIT_ECPDU:
+ assert((port->ecp.tx.state == ECP_TX_INIT_TRANSMIT) ||
+ (port->ecp.tx.state == ECP_TX_WAIT_FOR_ACK) ||
+ (port->ecp.tx.state == ECP_TX_REQUEST_PDU));
+ break;
+ case ECP_TX_WAIT_FOR_ACK:
+ assert(port->ecp.tx.state == ECP_TX_TRANSMIT_ECPDU);
+ break;
+ case ECP_TX_REQUEST_PDU:
+ assert(port->ecp.tx.state == ECP_TX_WAIT_FOR_ACK);
+ break;
+ default:
+ printf("ERROR: The ECP_TX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", port->ifname);
+ }
+
+ printf("%s(%i)-%s: state change %s -> %s\n", __func__, __LINE__,
+ port->ifname, ecp_tx_states[port->ecp.tx.state], ecp_tx_states[newstate]);
+
+ port->ecp.tx.state = newstate;
+ return;
+}
+
+/* ecp_set_tx_state - sets the ecp tx sm state
+ * @port: currently used port
+ *
+ * returns true or false
+ *
+ * switches the state machine to the next state depending on the input
+ * variables. returns true or false depending on wether the state machine
+ * can be run again with the new state or can stop at the current state.
+ */
+static bool ecp_set_tx_state(struct port *port)
+{
+ if ((port->portEnabled == false) && (port->prevPortEnabled == true)) {
+ printf("set_tx_state: port was disabled\n");
+ ecp_tx_change_state(port, ECP_TX_INIT_TRANSMIT);
+ }
+ port->prevPortEnabled = port->portEnabled;
+
+ switch (port->ecp.tx.state) {
+ case ECP_TX_IDLE:
+ if (port->portEnabled) {
+ ecp_tx_change_state(port, ECP_TX_INIT_TRANSMIT);
+ return true;
+ }
+ return false;
+ case ECP_TX_INIT_TRANSMIT:
+ if (port->portEnabled && ((port->adminStatus == enabledRxTx) ||
+ (port->adminStatus == enabledTxOnly))) {
+ ecp_tx_change_state(port, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ return false;
+ case ECP_TX_TRANSMIT_ECPDU:
+ if ((port->adminStatus == disabled) ||
+ (port->adminStatus == enabledRxOnly)) {
+ ecp_tx_change_state(port, ECP_TX_INIT_TRANSMIT);
+ return true;
+ }
+ ecp_tx_change_state(port, ECP_TX_WAIT_FOR_ACK);
+ return true;
+ case ECP_TX_WAIT_FOR_ACK:
+ if (port->ecp.ackTimerExpired) {
+ port->ecp.retries++;
+ if (port->ecp.retries < ECP_MAX_RETRIES) {
+ ecp_somethingChangedLocal(port, VDP_PROFILE_REQ);
+ ecp_tx_change_state(port, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ if (port->ecp.retries == ECP_MAX_RETRIES) {
+ printf("%s(%i)-%s: 1 \n", __func__, __LINE__,
+ port->ifname);
+ ecp_tx_change_state(port, ECP_TX_REQUEST_PDU);
+ return true;
+ }
+ }
+ if (port->ecp.ackReceived && port->ecp.seqECPDU == port->ecp.lastSequence) {
+ port->ecp.ackReceived = false;
+ ecp_tx_change_state(port, ECP_TX_REQUEST_PDU);
+ return true;
+ }
+ return false;
+ case ECP_TX_REQUEST_PDU:
+ if (port->ecp.tx.localChange & VDP_PROFILE_REQ) {
+ ecp_tx_change_state(port, ECP_TX_TRANSMIT_ECPDU);
+ return true;
+ }
+ return false;
+ default:
+ printf("ERROR: The TX State Machine is broken!\n");
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", port->ifname);
+ return false;
+ }
+}
+
+/* ecp_tx_run_sm - state machine for ecp tx
+ * @port: currently used port
+ *
+ * no return value
+ *
+ * runs the state machine for ecp tx.
+ */
+void ecp_tx_run_sm(struct port *port)
+{
+ do {
+ printf("%s(%i)-%s: ecp_tx - %s\n", __func__, __LINE__,
+ port->ifname, ecp_tx_states[port->ecp.tx.state]);
+
+ switch(port->ecp.tx.state) {
+ case ECP_TX_IDLE:
+ break;
+ case ECP_TX_INIT_TRANSMIT:
+ ecp_tx_Initialize(port);
+ break;
+ case ECP_TX_TRANSMIT_ECPDU:
+ ecp_tx_create_frame(port);
+ ecp_tx_start_ackTimer(port);
+ break;
+ case ECP_TX_WAIT_FOR_ACK:
+ if (port->ecp.ackReceived) {
+ printf("%s(%i)-%s: ECP_TX_WAIT_FOR_ACK ackReceived\n", __func__, __LINE__,
+ port->ifname);
+ printf("%s(%i)-%s: 2: seqECPDU %x lastSequence %x \n", __func__, __LINE__,
+ port->ifname, port->ecp.seqECPDU, port->ecp.lastSequence);
+ port->ecp.tx.localChange = 0;
+ ecp_tx_stop_ackTimer(port);
+ ecp_rx_ProcessFrame(port);
+ }
+ break;
+ case ECP_TX_REQUEST_PDU:
+ port->ecp.retries = 0;
+ port->ecp.lastSequence++;
+ printf("%s(%i)-%s: ECP_TX_REQUEST_PDU lastSequence %x\n", __func__, __LINE__,
+ port->ifname, port->ecp.lastSequence++);
+ break;
+ default:
+ printf("%s(%i): ERROR The TX State Machine is broken!\n", __func__,
+ __LINE__);
+ log_message(MSG_ERR_TX_SM_INVALID, "%s", port->ifname);
+ }
+ } while (ecp_set_tx_state(port) == true);
+
+ return;
+}
diff --git a/include/lldp_evb.h b/include/lldp_evb.h
index de39fd3..3ca7aa9 100644
--- a/include/lldp_evb.h
+++ b/include/lldp_evb.h
@@ -37,6 +37,12 @@ typedef enum {
EVB_CONFIRMATION
} evb_state;
+#define RTE 13
+/* retransmission granularity (RTG) in microseconds */
+#define EVB_RTG 10
+/* retransmission multiplier (RTM) */
+#define EVB_RTM(rte) (2<<(RTE-1))
+
struct tlv_info_evb {
u8 oui[3];
u8 sub;
diff --git a/include/lldp_vdp.h b/include/lldp_vdp.h
new file mode 100644
index 0000000..43200af
--- /dev/null
+++ b/include/lldp_vdp.h
@@ -0,0 +1,157 @@
+/*******************************************************************************
+
+ implementation of according to IEEE 802.1Qbg
+ (c) Copyright IBM Corp. 2010
+
+ Author(s): Jens Osterkamp <jens@linux.vnet.ibm.com>
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope it will be useful, but WITHOUT
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ more details.
+
+ You should have received a copy of the GNU General Public License along with
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
+*******************************************************************************/
+
+#ifndef _LLDP_VDP_H
+#define _LLDP_VDP_H
+
+#include "lldp_mod.h"
+
+#define LLDP_MOD_VDP OUI_IEEE_8021Qbg+1
+
+#define VDP_MODE_PREASSOCIATE 0x0
+#define VDP_MODE_PREASSOCIATE_WITH_RR 0x1
+#define VDP_MODE_ASSOCIATE 0x2
+#define VDP_MODE_DEASSOCIATE 0x3
+
+#define VDP_RESPONSE_SUCCESS 0x0
+#define VDP_RESPONSE_INVALID_FORMAT 0x1
+#define VDP_RESPONSE_INSUFF_RESOURCES 0x2
+#define VDP_RESPONSE_UNUSED_VTID 0x3
+#define VDP_RESPONSE_VTID_VIOLATION 0x4
+#define VDP_RESPONSE_VTID_VER_VIOLATION 0x5
+#define VDP_RESPONSE_OUT_OF_SYNC 0x6
+
+enum {
+ VDP_PROFILE_NOCHANGE = 0,
+ VDP_PROFILE_REQ,
+ VDP_PROFILE_ACK,
+ VDP_PROFILE_NACK,
+};
+
+#define VDP_MACVLAN_FORMAT_1 1
+
+#define VDP_TRANSMISSION_TIMER 3*EVB_RTM(RTE)*EVB_RTG
+#define VDP_TRANSMISSION_DIVIDER 10000
+
+#define VDP_ROLE_STATION 0
+#define VDP_ROLE_BRIDGE 1
+
+enum {
+ VSI_UNASSOCIATED = 0,
+ VSI_ASSOC_PROCESSING,
+ VSI_ASSOCIATED,
+ VSI_PREASSOC_PROCESSING,
+ VSI_PREASSOCIATED,
+ VSI_DEASSOC_PROCESSING,
+ VSI_EXIT,
+};
+
+static char *vsi_states[] = {
+ "VSI_UNASSOCIATED",
+ "VSI_ASSOC_PROCESSING",
+ "VSI_ASSOCIATED",
+ "VSI_PREASSOC_PROCESSING",
+ "VSI_PREASSOCIATED",
+ "VSI_DEASSOC_PROCESSING",
+ "VSI_EXIT"
+};
+
+struct mac_vlan {
+ u8 mac[6];
+ u16 vlan;
+} __attribute__ ((__packed__));
+
+struct tlv_info_vdp {
+ u8 oui[3];
+ u8 sub;
+ u8 mode;
+ u8 response;
+ u8 mgrid;
+ u8 id[3];
+ u8 version;
+ u8 instance[16];
+ u8 format;
+ u16 entries;
+ struct mac_vlan mac_vlan;
+} __attribute__ ((__packed__));
+
+struct vsi_profile {
+ int mode;
+ int response;
+ u8 mgrid;
+ u8 id[3];
+ u8 version;
+ u8 instance[16];
+ u8 mac[6]; /* TODO: currently only one MAC/VLAN pair supported, more later */
+ u16 vlan;
+ struct port *port;
+ int ackTimerExpired;
+ int ackReceived;
+ int state;
+ int localChange;
+ LIST_ENTRY(vsi_profile) profile;
+};
+
+struct vdp_data {
+ char ifname[IFNAMSIZ];
+ struct unpacked_tlv *vdp;
+ int role;
+ LIST_HEAD(profile_head, vsi_profile) profile_head;
+ LIST_ENTRY(vdp_data) entry;
+};
+
+struct vdp_user_data {
+ LIST_HEAD(vdp_head, vdp_data) head;
+};
+
+struct lldp_module *vdp_register(void);
+void vdp_unregister(struct lldp_module *mod);
+struct vdp_data *vdp_data(char *ifname);
+struct packed_tlv *vdp_gettlv(struct port *port, struct vsi_profile *profile);
+void vdp_vsi_sm_station(struct vsi_profile *profile);
+struct vsi_profile *vdp_add_profile(struct vsi_profile *profile);
+
+#define MAC_ADDR_STRLEN 18
+#define INSTANCE_STRLEN 32
+
+#define PRINT_PROFILE(s, p) \
+{ int c; \
+ c = sprintf(s, "\nmode: %i\n", p->mode); s += c; \
+ c = sprintf(s, "response: %i\n", p->response); s += c; \
+ c = sprintf(s, "state: %i\n", p->state); s += c; \
+ c = sprintf(s, "mgrid: %i\n", p->mgrid); s += c; \
+ c = sprintf(s, "id: %x%x%x\n", p->id[2], p->id[1], p->id[0]); \
+ s += c; \
+ c = sprintf(s, "version: %i\n", p->version); s += c; \
+ char instance[INSTANCE_STRLEN+2]; \
+ instance2str(p->instance, instance, sizeof(instance)); \
+ c = sprintf(s, "instance: %s\n", &instance); s += c; \
+ char macbuf[MAC_ADDR_STRLEN+1]; \
+ mac2str(p->mac, macbuf, MAC_ADDR_STRLEN); \
+ c = sprintf(s, "mac: %s\n", macbuf); s += c; \
+ c = sprintf(s, "vlan: %i\n\n", p->vlan); s += c; \
+}
+
+#endif /* _LLDP_VDP_H */
diff --git a/lldp/l2_packet.h b/lldp/l2_packet.h
index 16f3683..0962429 100644
--- a/lldp/l2_packet.h
+++ b/lldp/l2_packet.h
@@ -36,6 +36,8 @@
#define ETH_P_LLDP 0x88cc
+/* TODO: use extended ethertype until final ethertype is available */
+#define ETH_P_ECP 0x88b7
#define ETH_FRAME_LEN 1514
diff --git a/lldp/ports.h b/lldp/ports.h
index 51de561..2c6969c 100644
--- a/lldp/ports.h
+++ b/lldp/ports.h
@@ -136,21 +136,40 @@ struct porttlvs{
struct unpacked_tlv *last_peer;
};
+struct ecp {
+ struct l2_packet_data *l2;
+ int sequence;
+ int retries;
+ int ackReceived;
+ int ackTimerExpired;
+ u16 lastSequence;
+ u16 seqECPDU;
+ struct portrx rx;
+ struct porttx tx;
+ struct portstats stats;
+};
+
struct port {
char *ifname;
u8 hw_resetting;
u8 portEnabled;
u8 prevPortEnabled;
u8 adminStatus;
- u8 rxChanges;
- u16 lldpdu;
+
+ /* protocol specific */
struct l2_packet_data *l2;
struct portrx rx;
struct porttx tx;
- struct porttlvs tlvs;
struct portstats stats;
struct porttimers timers;
+ u8 rxChanges;
+ u16 lldpdu;
struct msap msap;
+
+ /* not sure */
+ struct porttlvs tlvs;
+
+ struct ecp ecp;
struct port *next;
};
diff --git a/lldp_evb.c b/lldp_evb.c
index 74cfa34..22a95af 100644
--- a/lldp_evb.c
+++ b/lldp_evb.c
@@ -360,6 +360,8 @@ static void evb_statemachine(struct evb_data *ed, struct tlv_info_evb *tie)
* different parameters ? Check parameters. switch state back to
* EVB_CONFIGURE ? */
printf("%s: state -> EVB_CONFIRMATION\n", __func__);
+ if (ed->tie->scap & LLDP_EVB_CAPABILITY_PROTOCOL_ECP)
+ ecp_init(ed->ifname);
break;
default:
fprintf(stderr, "EVB statemachine reached invalid state !\n");
--
1.7.1
^ permalink raw reply related
* implementation of IEEE 802.1Qbg in lldpad, part2
From: Jens Osterkamp @ 2010-06-17 13:57 UTC (permalink / raw)
To: e1000-eedc, virtualization, evb; +Cc: chrisw
Hi,
This series of patches contains the second part of an initial implementation of
the IEEE 802.1Qbg standard: code for the exchange of VDP VSI TLVs between a
host with virtual machines and an adjacent switch.
VDP profiles consisting of mode,mgrid,typeid,typeidversion,instanceid,mac,vlan
can be given to lldpad with lldptool.
A way to deliver profiles to lldpad from libvirt using netlink messages is
currently under development and not yet included in this series.
The profiles are then processed through the VDP/VSI and ECP state machines and
sent out as ECP frames.
ACK frames are received and processed through ECP and VDP/VSI state machines.
It implements a bridge role for a port together with a lldptool command to
switch a port to the bridge role.
The patch applies to lldpad 0.9.34 and still contains code to log low-level
protocol activity more verbosely than it would be necessary in the final
version.
Things that are still missing but planned:
- adapt to final ECP ethertype. code currently uses extended
ethertype (0x88b7) plus OUI.
- more testing
Next steps
- interface to allow registration of profiles with libvirt
- implementation of CDCP
Please review and comment !
Thanks !
Jens
_______________________________________________
Virtualization mailing list
Virtualization@lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/virtualization
^ permalink raw reply
* Re: [PATCH for-2.6.35] virtio_net: fix oom handling on tx
From: Rusty Russell @ 2010-06-15 4:23 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: Shirley Ma, Jiri Pirko, netdev, linux-kernel, virtualization,
Stephen Hemminger, Sridhar Samudrala
In-Reply-To: <20100610190343.GC4044@redhat.com>
On Fri, 11 Jun 2010 04:33:43 am Michael S. Tsirkin wrote:
> On Thu, Jun 10, 2010 at 10:46:53AM -0700, Stephen Hemminger wrote:
> > It makes more sense to have the device increment tx_droppped,
> > and return NETDEV_TX_OK. Skip the message (or make it a pr_debug()).
> > Network devices do not guarantee packet delivery, and if out of
> > resources then holding more data in the
> > queue is going to hurt not help the situation.
Yes, actually oom should be a ratelimited message and TX_OK, the other
case is a "should never happen" logic bug which warrants an error and
I don't care what it returns (whatever's easiest).
Please fix both at once since you're touching it.
Thanks,
Rusty.
^ permalink raw reply
* potential race in virtio ring?
From: Michael S. Tsirkin @ 2010-06-14 13:59 UTC (permalink / raw)
To: virtualization, Rusty Russell, Jiri Pirko, Shirley Ma, netdev
Hi!
I was going over the vring code and noticed, that
the ring has this check:
irqreturn_t vring_interrupt(int irq, void *_vq)
{
struct vring_virtqueue *vq = to_vvq(_vq);
if (!more_used(vq)) {
pr_debug("virtqueue interrupt with no work for %p\n", vq);
return IRQ_NONE;
static inline bool more_used(const struct vring_virtqueue *vq)
{
return vq->last_used_idx != vq->vring.used->idx;
}
My concern is that with virtio net, more_used is called
on a CPU different from the one that polls the vq.
This might mean that last_used_idx value might be stale.
Could this lead to a missed interrupt?
Thanks,
--
MST
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox