Dummy I2C device triggers probe fuction - linux-device-driver

I'm writing a driver for a CMOS sensor. This device requires multiple I2C settings, but I'm also controlling IO for it through an FPGA with I2C. Specifically, this FPGA contains an IO needing controlling when starting a stream. Because it is a custom device, I'm trying to create a dummy i2c_client structure. I have the following (modified) function in the driver to start a stream.
static int imx267_start_streaming(struct tegracam_device *tc_dev)
{
struct imx267 *priv = (struct imx267 *)tegracam_get_privdata(tc_dev);
int err;
struct i2c_client *fpgai2c;
struct i2c_adapter *adapterused;
s32 val;
u8 bytetowrite;
u8 regaddr_toread = 0xFF;
printk(KERN_INFO "%s\n", __FUNCTION__);
printk("[IMX267]: We can get the following information from the tc_dev struct, nr = %d, name = %s\n",tc_dev->client->adapter->nr,tc_dev->client->adapter->name);
adapterused = i2c_get_adapter(tc_dev->client->adapter->nr); //First we need to get a handle on the adapter beeing used by the currently starting sensor
fpgai2c = i2c_new_dummy(adapterused,0x48); //Create dummy I2C client to access FPGA I2C on same adapter as currently starting sensor
printk("[IMX267]: We can get the following information from the adapterused struct, nr = %d, name = %s\n",tc_dev->client->adapter->nr,tc_dev->client->adapter->name);
val = i2c_smbus_read_byte_data(fpgai2c,regaddr_toread); //Read register for read modify write
bytetowrite = (u8)val;
printk("[IMX267]: register 0x%x from FPGA reads 0x%x(%d). Casting it (s32) to u8 gives a value 0x%x(%d)\n",regaddr_toread,val,val,bytetowrite,bytetowrite);
bytetowrite |= 0x40;
printk("[IMX267]: after OR with 0x40 value is 0x%x(%d), name of client is now %s",bytetowrite,bytetowrite,fpgai2c->name);
val = i2c_smbus_write_byte_data(fpgai2c,regaddr_toread,bytetowrite);
printk("[IMX267]: FPGA I2C write gave following results 0x%x(%d)\n",val,val);
usleep_range(1000, 2000); // Falco: delay before stream start
if (test_mode) {
err = imx267_write_table(priv,
mode_table[IMX267_MODE_TEST_PATTERN]);
} else {
err = imx267_write_table(priv, mode_table[IMX267_MODE_START_STREAM]);
}
if (err) {
printk(KERN_ERR "%s in write table\n", __FUNCTION__);
return err;
}
i2c_unregister_device(fpgai2c); //Unregister temporary client to release FPGA control for now
return 0;
}
The part of using the FPGA via I2C to control the IO works, I can see the stream starting. However, looking in dmesg, I also see it has triggered the probe function of the sensor driver:
[ 62.884372] imx267_start_streaming
[ 62.884377] [IMX267]: We can get the following information from the tc_dev struct, nr = 30, name = i2c-2-mux (chan_id 0)
[ 62.885442] [IMX267]: We can get the following information from the adapterused struct, nr = 30, name = i2c-2-mux (chan_id 0)
[ 62.885788] [IMX267]: register 0xFF from FPGA reads 0x00(0). Casting it (s32) to u8 gives a value 0x00(0)
[ 62.885791] [IMX267]: after OR with 0xF0 value is 0xF0(240), name of client is now dummy
[ 62.886077] [IMX267]: FPGA I2C write gave following results 0x0(0)
[ 62.886605] imx267_probe: called
[ 62.886608] imx267_probe: module version: 0.04
[ 62.886614] imx267 32-001a: [IMX267]: probing v4l2 sensor at addr 0x1a with name 'imx267'.
[ 62.886655] imx267_parse_dt: called
[ 62.886657] imx267_parse_dt: OK of_match_device
[ 62.886660] imx267_parse_dt of_property_read_string (mclk) found: extperiph1
[ 62.886686] imx267 32-001a: reset-gpios not found 0
[ 62.888100] IMX267: Now writing table. Number of modes: 3, mode_prop_idx = 0, active_w/width = 4096, table = 12288
[ 62.891612] imx267 32-001a: tegra camera driver registration failed
The whole of imx267_probe and _parse shouldn't be there, this has already been done at the initialisation of the driver. The odd thing is that the name of i2c_client differs, the name of the client I created is dummy but the name used by the imx267_probe function is called imx267 (hence why the probe function of the image driver has been triggered?) The probe function is defined as:
static int imx267_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct device *dev = &client->dev;
struct tegracam_device *tc_dev;
struct imx267 *priv;
int err;
printk("%s: called\n", __FUNCTION__);
printk("%s: module version: %s", __FUNCTION__, IMX267_MODULE_VERSION);
dev_info(&client->dev, "[IMX267]: probing v4l2 sensor at addr 0x%0x with name '%s'.\n",
client->addr,client->name);
...
I've also tried creating a dummy i2c_board_info and using i2c_new_device, but it results into the same effect of the probe function of the sensor driver being triggered..
I think I must be missing some information that needs change in the structs of the i2c, but I simply haven't got a clue any more where to look.
Kernel version used is 4.9.140
Edit: I also found the function i2c_new_probed_device for which you can pass your own probing function. This resulted into the dummy function beeing triggered, but still also the sensor probe function..
int dummy_probe(struct i2c_adapter *useless, unsigned short addr) //Needs to follow naming conventions
{
printk("IMX[267]: dummy_probe function triggered!\n");
return 1;
}
static int imx267_start_streaming(struct tegracam_device *tc_dev)
{
struct imx267 *priv = (struct imx267 *)tegracam_get_privdata(tc_dev);
int err;
static struct i2c_board_info dummy_board_info = {
I2C_BOARD_INFO("dummy", 0x48),
};
unsigned short addr_list[1] = { 0x48 };
struct i2c_client *fpgai2c;
struct i2c_adapter *adapterused;
s32 val;
u8 bytetowrite;
u8 regaddr_toread = 0xFF;
printk(KERN_INFO "%s\n", __FUNCTION__);
printk("[IMX267]: We can get the following information from the tc_dev struct, nr = %d, name = %s\n",tc_dev->client->adapter->nr,tc_dev->client->adapter->name);
adapterused = i2c_get_adapter(tc_dev->client->adapter->nr); //First we need to get a handle on the adapter beeing used by the currently starting sensor
//fpgai2c = i2c_new_dummy_device(adapterused,0x48); //Create dummy I2C client to access FPGA I2C on same adapter as currently starting sensor
fpgai2c = i2c_new_probed_device(adapterused,&dummy_board_info,addr_list,(*dummy_probe));
printk("[IMX267]: We can get the following information from the adapterused struct, nr = %d, name = %s\n",tc_dev->client->adapter->nr,tc_dev->client->adapter->name);
...
Result:
3
[ 76.276400] imx267_start_streaming
[ 76.276404] [IMX267]: We can get the following information from the tc_dev struct, nr = 30, name = i2c-2-mux (chan_id 0)
[ 76.276417] IMX[267]: dummy_probe function triggered!
[ 76.277201] [IMX267]: We can get the following information from the adapterused struct, nr = 30, name = i2c-2-mux (chan_id 0)
[ 76.277540] [IMX267]: register from FPGA reads . Casting it (s32) to u8 gives a value
[ 76.277543] [IMX267]: after OR with value is , name of client is now dummy
[ 76.277826] [IMX267]: FPGA I2C write gave following results 0x0(0)
[ 76.278820] imx267_probe: called
[ 76.278824] imx267_probe: module version: 0.04
[ 76.278829] imx267 32-001a: [IMX267]: probing v4l2 sensor at addr 0x1a with name 'imx267'.
[ 76.278868] imx267_parse_dt: called
[ 76.278870] imx267_parse_dt: OK of_match_device
[ 76.278873] imx267_parse_dt of_property_read_string (mclk) found: extperiph1
[ 76.278898] imx267 32-001a: reset-gpios not found 0
[ 76.279890] IMX267: Now writing table. Number of modes: 3, mode_prop_idx = 0, active_w/width = 4096, table = 12288
[ 76.283825] imx267 32-001a: tegra camera driver registration failed

Related

Linux Kernel Module dev_get_drvdata Function Always Returns NULL

I have the following kernel module probe function (simplified to show the relevant parts):
static int qnap_ec_probe(struct platform_device* platform_dev)
{
// Allocate memory for the custom data and associate with the device
struct qnap_ec_platform_drv_data* platform_drv_data;
platform_drv_data = devm_kzalloc(&platform_dev->dev, sizeof(struct qnap_ec_platform_drv_data),
GFP_KERNEL);
// Add the custom data to the platform device
platform_set_drvdata(platform_dev, platform_drv_data);
// Register the platform device
devm_hwmon_device_register_with_info(&platform_dev->dev, "qnap_ec", NULL,
&qnap_ec_hwmon_chip_info, NULL);
return 0;
}
and the following hwmon read callback function:
static int qnap_ec_hwmon_read(struct device* dev, enum hwmon_sensor_types type, u32 attribute,
int channel, long* value)
{
struct qnap_ec_platform_drv_data* platform_drv_data;
platform_drv_data = dev_get_drvdata(dev);
if (platform_drv_data == NULL)
{
return -1;
}
return 0;
}
Unfortunately the second function always returns -1 because the dev_get_drvdata function always returns NULL. For some reason the data that's associated with the device in the probe function using platform_set_drvdata doesn't make it into the hwmon read callback function. Am I missing a step in associating this data with the device? What could be causing this issue?

eBPF packet monitor losing some packets when 'ping -f'ed

I wrote this program to capture all the packets that come into my network interface.
It seems to work fine with things like ping [IP].
It also works fine with ping -f [IP] -c 10.
However, when the number of packets that are pinged goes up to like 200, the program sees to lost some packets.
Is this a natural limit of eBPF or am I doing something wrong?
Here's the code :
Also, when I ping -f [IP] -c 500, it also outputs : "Possibly lost 10 samples" or "Possibly lost 34 samples"
from bcc import BPF
# Network interface to be monoitored
INTERFACE = "my_interface"
bpf_text = """
#include <uapi/linux/ptrace.h>
#include <net/sock.h>
#include <bcc/proto.h>
#include <linux/bpf.h>
#define IP_TCP 6
#define IP_UDP 17
#define IP_ICMP 1
#define ETH_HLEN 14
BPF_PERF_OUTPUT(skb_events); // has to be delcared outside any function
BPF_ARRAY(black_list, u64, 5);
int packet_monitor(struct __sk_buff *skb) {
u8 *cursor = 0;
u32 saddr;
u32 daddr;
u32 ttl;
u32 hchecksum;
u64 magic = 111;
u64 magic2 = 111;
struct ethernet_t *ethernet = cursor_advance(cursor, sizeof(*ethernet));
if (!(ethernet -> type == 0x0800)) {
return 0; // drop
}
struct ip_t *ip = cursor_advance(cursor, sizeof(*ip));
/*
if (ip->nextp != IP_TCP)
{
if (ip -> nextp != IP_UDP)
{
if (ip -> nextp != IP_ICMP)
return 0;
}
}
*/
skb_events.perf_submit_skb(skb, skb -> len, &magic, sizeof(magic));
saddr = ip -> src;
daddr = ip -> dst;
ttl = ip -> ttl;
hchecksum = ip -> hchecksum;
magic = ip -> src;
magic2 = ip -> dst;
skb_events.perf_submit_skb(skb, skb->len, &magic, sizeof(magic)); // this one parses number as a hex to the user space
skb_events.perf_submit_skb(skb, skb->len, &magic2, sizeof(magic2)); // can send multiple values like this
bpf_trace_printk("saddr = %llu, daddr = %llu, ttl = %llu", saddr, daddr, ttl);
// bpf_trace_printk("Incoming packet!!\\n");
return -1;
}
"""
from ctypes import *
import ctypes as ct
import sys
import socket
import os
import struct
def print_skb_event(cpu, data, size):
class SkbEvent(ct.Structure):
_fields_ = [ ("magic", ct.c_uint32), ("magic2", ct.c_uint32)]
skb_event = ct.cast(data, ct.POINTER(SkbEvent)).contents
print("- : ")
print("%d" % (skb_event.magic))
bpf = BPF(text=bpf_text)
function_skb_matching = bpf.load_func("packet_monitor", BPF.SOCKET_FILTER)
BPF.attach_raw_socket(function_skb_matching, INTERFACE)
bpf["skb_events"].open_perf_buffer(print_skb_event)
black_list = bpf.get_table("black_list") # retrieve blacklist list
try:
while True :
bpf.perf_buffer_poll() # value = bpf.perf_buffer_poll() function does not return any function and therefore, doesn't work
except KeyboardInterrupt:
pass
Yes, that's a limitation of the perf ring buffer. If the BPF program produce events on the ring buffer faster than the userspace (Python) process can consume them, some events will be lost (overwritten since it's a ring). The Possibly lost XX samples message is a notification of this happening.
I would first recommend that you try to group your multiple skb_events.perf_submit_skb calls into a single one on the BPF side. That may help. Otherwise, you can try to aggregate data on the BPF side to have less information sent to the Python side.

EBPF Newbie: Need Help, facing an error while loading a EBF code

I wrote a bpf code and compiled with clang, while trying to load, I face an error. I am not able to understand why and how to resolve it, need experts advice.
I am running this code in a VM
OS : Ubuntu 18.04.2
Kernel : Linux 4.18.0-15-generic x86_64
I tried simple programs and I able to load but not with this program.
static __inline int clone_netflow_record (struct __sk_buff *skb, unsigned long dstIpAddr)
{
return XDP_PASS;
}
static __inline int process_netflow_records( struct __sk_buff *skb)
{
int i = 0;
#pragma clang loop unroll(full)
for (i = 0; i < MAX_REPLICATIONS; i++) {
clone_netflow_record (skb, ipAddr[i]);
}
return XDP_DROP;
}
__section("action")
static int probe_packets(struct __sk_buff *skb)
{
/* We will access all data through pointers to structs */
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
if (data > data_end)
return XDP_DROP;
/* for easy access we re-use the Kernel's struct definitions */
struct ethhdr *eth = data;
struct iphdr *ip = (data + sizeof(struct ethhdr));
/* Only actual IP packets are allowed */
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_DROP;
/* If Netflow packets process it */
if (ip->protocol != IPPROTO_ICMP)
{
process_netflow_records (skb);
}
return XDP_PASS;
}
ERROR Seen:
$ sudo ip link set dev enp0s8 xdp object clone.o sec action
Prog section 'action' rejected: Permission denied (13)!
- Type: 6
- Instructions: 41 (0 over limit)
- License: GPL
Verifier analysis:
0: (bf) r2 = r1
1: (7b) *(u64 *)(r10 -16) = r1
2: (79) r1 = *(u64 *)(r10 -16)
3: (61) r1 = *(u32 *)(r1 +76)
invalid bpf_context access off=76 size=4
Error fetching program/map!
The kernel verifier that enforces checks on your program in the Linux kernel ensures that no out-of-bound accesses are attempted. Your program is rejected because it may trigger such out-of-bound access.
If we have a closer look at your snippet:
void *data = (void *)(long)skb->data;
void *data_end = (void *)(long)skb->data_end;
So here we get pointers to data (start of packet) and data_end.
if (data > data_end)
return XDP_DROP;
The above check is unnecessary (data will not be higher than data_end). But there's another check you should do here instead. Let's see below:
/* for easy access we re-use the Kernel's struct definitions */
struct ethhdr *eth = data;
struct iphdr *ip = (data + sizeof(struct ethhdr));
/* Only actual IP packets are allowed */
if (eth->h_proto != __constant_htons(ETH_P_IP))
return XDP_DROP;
What you do here is, first, making eth and ip point to the start of the packet and (supposedly) the start of the IP header. This step is fine. But then, you try to dereference eth to access its h_proto field.
Now, what would happen if the packet was not Ethernet, and it was not long enough to have an h_proto field in it? You would try to read some data outside of the bounds of the packet, this is the out-of-bound access I mentioned earlier. Note that it does not mean your program actually tried to read this data (as a matter of fact, I don't see how you could get a packet shorter than 14 bytes). But from the verifier's point of view, it is technically possible that this forbidden access could occur, so it rejects your program. This is what it means with invalid bpf_context access: your code tries to access the context (for XDP: packet data) in an invalid way.
So how do we fix that? The check that you should have before trying to dereference the pointer should not be on data > data_end, it should be instead:
if (data + sizeof(struct ethhdr) > data_end)
return XDP_DROP;
So if we pass the check without returning XDP_DROP, we are sure that the packet is long enough to contain a full struct ethhdr (and hence a h_proto field).
Note that a similar check on data + sizeof(struct ethhdr) + sizeof(struct iphdr) will be necessary before trying to dereference ip, for the same reason. Each time you try to access data from the packet (the context), you should make sure that your packet is long enough to dereference the pointer safely.

Linux socket hardware timestamping

I'm working on a project researching about network synchronisation. Since I want to achieve the best performance I'm trying to compare software timestamping results with hardware timestamping ones.
I have followed this previously commented issue: Linux kernel UDP reception timestamp but after several tests I got some problems when trying to get hardware reception timestamps.
My scenario is composed of 2 devices, a PC and a Gateworks Ventana board, both devices are supposed to be waiting for packets to be broadcasted in their network and timestamping their reception times, I have tried this code (some parts have been omitted):
int rc=1;
int flags;
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RAW_HARDWARE;
rc = setsockopt(sock, SOL_SOCKET,SO_TIMESTAMPING, &flags, sizeof(flags));
rc = bind(sock, (struct sockaddr *) &serv_addr, sizeof(serv_addr));
struct msghdr msg;
struct iovec iov;
char pktbuf[2048];
char ctrl[CMSG_SPACE(sizeof(struct timespec))];
struct cmsghdr *cmsg = (struct cmsghdr *) &ctrl;
msg.msg_control = (char *) ctrl;
msg.msg_controllen = sizeof(ctrl);
msg.msg_name = &serv_addr;
msg.msg_namelen = sizeof(serv_addr);
msg.msg_iov = &iov;
msg.msg_iovlen = 1;
iov.iov_base = pktbuf;
iov.iov_len = sizeof(pktbuf);
//struct timeval time_kernel, time_user;
//int timediff = 0;
FILE *f = fopen("server.csv", "w");
if (f == NULL) {
error("Error opening file!\n");
exit(1);
}
fprintf(f, "Time\n");
struct timespec ts;
int level, type;
int i;
for (i = 0; i < 10; i++) {
rc = recvmsg(sock, &msg, 0);
for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL; cmsg = CMSG_NXTHDR(&msg, cmsg))
{
level = cmsg->cmsg_level;
type = cmsg->cmsg_type;
if (SOL_SOCKET == level && SO_TIMESTAMPING == type) {
//ts = (struct timespec *) CMSG_DATA(cmsg);
memcpy(&ts, CMSG_DATA(cmsg), sizeof(ts));
printf("HW TIMESTAMP %ld.%09ld\n", (long)ts.tv_sec, (long)ts.tv_nsec);
}
}
}
printf("COMPLETED\n");
fclose(f);
close(sock);
return 0;
}
In both devices the output I get after receiving a packet:
HW TIMESTAMP 0.000000000
On the other hand if with the same code my flags are:
flags = SOF_TIMESTAMPING_RX_HARDWARE
| SOF_TIMESTAMPING_RX_SOFTWARE
| SOF_TIMESTAMPING_SOFTWARE;
I get proper timestamps:
HW TIMESTAMP 1551721801.970270543
However, they seem to be software-timestamping ones. What would be the correct solution / method to handle hardware timestamping for packets received?
First of all, use ethtool -T "your NIC" to make sure your hardware supports the hardware timestamping feature.
You need to explicitly tell the Linux to enable the hardware timestamping feature of your NIC. In order to to that, you need to have a ioctl() call.
What you have to do is to call it with SIOCSHWTSTAMP, which is a device request code to indicate which device you want to handle as well as what you want to do. For example, there is a code called CDROMSTOP to stop the cdrom drive.
You also need to use a ifreq struct to configure your NIC.
You need something like this:
struct ifreq ifconfig;
strncpy(config.ifr_name, "your NIC name", sizeof(ifconfig.ifr_name));
ioctl("your file descriptor" , SIOCSHWTSTAMP, &ifconfig);
Here are some pages that you can look up to:
ioctl manual page,
ifreq manual page,
Read part 3.

ensuring data-consistency of object

I'm having a problem with data-consistencies of objects e.g. how to deal with objects that get deleted while the user still has a reference to them.
simple pseudo-code example
node = graph.getNode(name)
node.destroy() < -- node gets destroyed
#or
graph.destroyNode(name)
node.getName() #<-- should complain that we're trying to reference an object that does not exist any more
a simple pseudo-code example would be
struct Node
{
/*...*/
};
typedef boost::shared_ptr<Node> NodePtr;
struct Graph
{
std::map<std::string,NodePtr> nodeMap;
NodePtr getNode(std::string name);
void removeNode(std::string name);
/*...*/
};
typedef boost::shared_ptr<Graph> GraphPtr;
// wrapper arround the the getNode function
object Graph_getNode( object obj, const std::string& key )
{
GraphPtr graphPtr = extract< GraphPtr >(obj);
return boost::python::api::object(graphPtr->getNode(key));
};
class_< Node,boost::noncopyable, NodePtr >( "Node", "node", no_init )
/*....*/
class_< Graph, bases<Node>, boost::noncopyable, GraphPtr >( "Graph", "graph", no_init )
.def("getNode", Graph_getNode, "get a node if it exists")
/*....*/
Are there any functions that I could define which are run on a Node-object every time it gets used where I could check if it is still valid ?
I hope this information is sufficient to understand my problem,
... thanks for reading!
Seb
As Node is exposed as being held by a boost::shared_ptr, the instance's lifetime can be affected by both C++ and Python. Python will keep the C++ Node object alive as long as a handle to the Python object holding the boost::shared_ptr exists.
Therefore, in the following code, lets start with the precondition that a Node named spam is held in graph. The code is annotated with the reference count for the C++ Node instance, as well as the Python Node instance.
# The node named 'spam' is held in
# graph's nodeMap.
# cpp count: 1; python count: 0
# Boost.Python creats a Python Node object
# that holds boost::shared_ptr<Node>.
node = graph.getNode('spam') # cpp count: 2; python count: 1
# Removes the boost::shared_ptr<Node> for
# 'spam' from graph.nodeMap.
graph.destroyNode('spam') # cpp count: 1; python count: 1
# The C++ Node is still alive because the
# Python Node holds a boost::shared_ptr<Node>
node.getName() # cpp count: 1; python count: 1
# When Python no longer has a handle to the
# Python Node object, it will be garbage
# collected, which in turn will destroy
# the held boost::shared_ptr<Node>
node = None # cpp count: 0; python count: 0
If node.getName() should not operate successfully on a destroyed node, then consider explicitly managing state. For example, node.destroy() would set state within node, and member-functions would check state before performing operations. If the state indicates the node has been destroyed, then raise an exception or handle it appropriately.
Here is a basic example demonstrating the lifetime:
#include <map>
#include <string>
#include <boost/make_shared.hpp>
#include <boost/python.hpp>
#include <boost/shared_ptr.hpp>
class Node
{
public:
Node(std::string name)
: name_(name)
{
std::cout << "Node()" << std::endl;
}
~Node()
{
std::cout << "~Node()" << std::endl;
}
std::string name() { return name_; }
private:
std::string name_;
};
class Graph
{
public:
// #brief Add node by name.
void add_node(std::string name)
{
nodes_.insert(make_pair(name, boost::make_shared<Node>(name)));
}
/// #brief Destroy node by name.
void destroy_node(std::string name)
{
nodes_.erase(name);
}
// #brief Get node by name.
boost::shared_ptr<Node> get_node(std::string name)
{
nodes_type::iterator result = nodes_.find(name);
return (result != nodes_.end())
? result->second
: boost::shared_ptr<Node>();
}
private:
typedef std::map<std::string, boost::shared_ptr<Node> > nodes_type;
nodes_type nodes_;
};
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<Node, boost::shared_ptr<Node>,
boost::noncopyable>("Node", python::no_init)
.def("name", &Node::name)
;
python::class_<Graph, boost::noncopyable>("Graph")
.def("add_node", &Graph::add_node)
.def("get_node", &Graph::get_node)
.def("destroy_node", &Graph::destroy_node)
;
}
And its usage in Python:
>>> from example import Graph
>>> graph = Graph()
>>> graph.add_node('spam')
Node()
>>> node = graph.get_node('spam')
>>> print node.name()
spam
>>> graph.destroy_node('spam')
>>> print node.name()
spam
>>> graph = None
>>> node = None
~Node()
Note how Node's destructor was only invoked once Python no longer had a handle to the object.