From 6842ec13d3cad1145e8826ef102a4a3dc6184435 Mon Sep 17 00:00:00 2001
From: Florian Pose <fp@igh-essen.com>
Date: Fri, 27 Oct 2006 09:19:58 +0000
Subject: [PATCH] FIX: Introduced destroy() functions for kobject-derived
 classes and thus fixed memory leak.

---
 master/canopen.c | 42 ++++++++++++++++++++++++++++++-----
 master/canopen.h |  3 +++
 master/domain.c  | 40 ++++++++++++++++++++-------------
 master/domain.h  |  3 +--
 master/fsm.c     |  8 +++----
 master/master.c  | 58 ++++++++++++++++++++++++++++++++++--------------
 master/master.h  |  5 +++--
 master/module.c  |  3 +--
 master/slave.c   | 40 +++++++++++++++++++++++----------
 master/slave.h   |  1 +
 10 files changed, 143 insertions(+), 60 deletions(-)

diff --git a/master/canopen.c b/master/canopen.c
index 6c726c22..ad144d8b 100644
--- a/master/canopen.c
+++ b/master/canopen.c
@@ -132,20 +132,36 @@ int ec_sdo_init(ec_sdo_t *sdo, /**< SDO */
 
 /**
    SDO destructor.
+   Clears and frees an SDO object.
 */
 
-void ec_sdo_clear(struct kobject *kobj /**< SDO's kobject */)
+void ec_sdo_destroy(ec_sdo_t *sdo /**< SDO */)
 {
-    ec_sdo_t *sdo = container_of(kobj, ec_sdo_t, kobj);
     ec_sdo_entry_t *entry, *next;
 
     // free all entries
     list_for_each_entry_safe(entry, next, &sdo->entries, list) {
         list_del(&entry->list);
-        kobject_del(&entry->kobj);
-        kobject_put(&entry->kobj);
+        ec_sdo_entry_destroy(entry);
     }
 
+    // destroy self
+    kobject_del(&sdo->kobj);
+    kobject_put(&sdo->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+   Clear and free SDO.
+   This method is called by the kobject,
+   once there are no more references to it.
+*/
+
+void ec_sdo_clear(struct kobject *kobj /**< SDO's kobject */)
+{
+    ec_sdo_t *sdo = container_of(kobj, ec_sdo_t, kobj);
+
     if (sdo->name) kfree(sdo->name);
 
     kfree(sdo);
@@ -215,7 +231,23 @@ int ec_sdo_entry_init(ec_sdo_entry_t *entry, /**< SDO entry */
 /*****************************************************************************/
 
 /**
-   SDO destructor.
+   SDO entry destructor.
+   Clears and frees an SDO entry object.
+*/
+
+void ec_sdo_entry_destroy(ec_sdo_entry_t *entry /**< SDO entry */)
+{
+    // destroy self
+    kobject_del(&entry->kobj);
+    kobject_put(&entry->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+   Clear and free SDO entry.
+   This method is called by the kobject,
+   once there are no more references to it.
 */
 
 void ec_sdo_entry_clear(struct kobject *kobj /**< SDO entry's kobject */)
diff --git a/master/canopen.h b/master/canopen.h
index 3720abde..ec78e84d 100644
--- a/master/canopen.h
+++ b/master/canopen.h
@@ -120,7 +120,10 @@ ec_sdo_request_t;
 /*****************************************************************************/
 
 int ec_sdo_init(ec_sdo_t *, uint16_t, ec_slave_t *);
+void ec_sdo_destroy(ec_sdo_t *);
+
 int ec_sdo_entry_init(ec_sdo_entry_t *, uint8_t, ec_sdo_t *);
+void ec_sdo_entry_destroy(ec_sdo_entry_t *);
 
 /*****************************************************************************/
 
diff --git a/master/domain.c b/master/domain.c
index 0776939f..f15fcf6b 100644
--- a/master/domain.c
+++ b/master/domain.c
@@ -130,6 +130,30 @@ int ec_domain_init(ec_domain_t *domain, /**< EtherCAT domain */
 
 /**
    Domain destructor.
+   Clears and frees a domain object.
+*/
+
+void ec_domain_destroy(ec_domain_t *domain /**< EtherCAT domain */)
+{
+    ec_datagram_t *datagram;
+
+    // dequeue datagrams
+    list_for_each_entry(datagram, &domain->datagrams, list) {
+        if (!list_empty(&datagram->queue)) // datagram queued?
+            list_del_init(&datagram->queue);
+    }
+
+    // destroy self
+    kobject_del(&domain->kobj);
+    kobject_put(&domain->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+   Clear and free domain.
+   This method is called by the kobject,
+   once there are no more references to it.
 */
 
 void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
@@ -463,22 +487,6 @@ void ec_domain_queue_datagrams(ec_domain_t *domain /**< EtherCAT domain */)
 
 /*****************************************************************************/
 
-/**
-   Dequeues all datagrams from the masters datagram queue.
-*/
-
-void ec_domain_dequeue_datagrams(ec_domain_t *domain /**< EtherCAT domain */)
-{
-    ec_datagram_t *datagram;
-
-    list_for_each_entry(datagram, &domain->datagrams, list) {
-        if (!list_empty(&datagram->queue)) // datagram queued?
-            list_del_init(&datagram->queue);
-    }
-}
-
-/*****************************************************************************/
-
 /**
    Formats attribute data for SysFS reading.
    \return number of bytes to read
diff --git a/master/domain.h b/master/domain.h
index fc4d7929..d6a5cdf1 100644
--- a/master/domain.h
+++ b/master/domain.h
@@ -76,11 +76,10 @@ struct ec_domain
 /*****************************************************************************/
 
 int ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
+void ec_domain_destroy(ec_domain_t *);
 
 int ec_domain_alloc(ec_domain_t *, uint32_t);
-
 void ec_domain_queue_datagrams(ec_domain_t *);
-void ec_domain_dequeue_datagrams(ec_domain_t *);
 
 /*****************************************************************************/
 
diff --git a/master/fsm.c b/master/fsm.c
index 461f4726..b5aff081 100644
--- a/master/fsm.c
+++ b/master/fsm.c
@@ -226,7 +226,7 @@ void ec_fsm_master_broadcast(ec_fsm_t *fsm /**< finite state machine */)
     if (topology_change && master->mode == EC_MASTER_MODE_IDLE) {
 
         ec_master_eoe_stop(master);
-        ec_master_clear_slaves(master);
+        ec_master_destroy_slaves(master);
 
         master->slave_count = datagram->working_counter;
 
@@ -241,14 +241,14 @@ void ec_fsm_master_broadcast(ec_fsm_t *fsm /**< finite state machine */)
             if (!(slave = (ec_slave_t *) kmalloc(sizeof(ec_slave_t),
                                                  GFP_ATOMIC))) {
                 EC_ERR("Failed to allocate slave %i!\n", i);
-                ec_master_clear_slaves(master);
+                ec_master_destroy_slaves(master);
                 fsm->master_state = ec_fsm_master_error;
                 return;
             }
 
             if (ec_slave_init(slave, master, i, i + 1)) {
                 // freeing of "slave" already done
-                ec_master_clear_slaves(master);
+                ec_master_destroy_slaves(master);
                 fsm->master_state = ec_fsm_master_error;
                 return;
             }
@@ -256,7 +256,7 @@ void ec_fsm_master_broadcast(ec_fsm_t *fsm /**< finite state machine */)
             if (kobject_add(&slave->kobj)) {
                 EC_ERR("Failed to add kobject.\n");
                 kobject_put(&slave->kobj); // free
-                ec_master_clear_slaves(master);
+                ec_master_destroy_slaves(master);
                 fsm->master_state = ec_fsm_master_error;
                 return;
             }
diff --git a/master/master.c b/master/master.c
index 1072ca6a..388118d0 100644
--- a/master/master.c
+++ b/master/master.c
@@ -55,6 +55,7 @@
 /*****************************************************************************/
 
 void ec_master_clear(struct kobject *);
+void ec_master_destroy_domains(ec_master_t *);
 void ec_master_sync_io(ec_master_t *);
 void ec_master_idle_run(void *);
 void ec_master_eoe_run(unsigned long);
@@ -222,8 +223,25 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
 
 /**
    Master destructor.
-   Removes all pending datagrams, clears the slave list, clears all domains
-   and frees the device.
+   Clears the kobj-hierarchy bottom up and frees the master.
+*/
+
+void ec_master_destroy(ec_master_t *master /**< EtherCAT master */)
+{
+    ec_master_destroy_slaves(master);
+    ec_master_destroy_domains(master);
+
+    // destroy self
+    kobject_del(&master->kobj);
+    kobject_put(&master->kobj); // free master
+}
+
+/*****************************************************************************/
+
+/**
+   Clear and free master.
+   This method is called by the kobject,
+   once there are no more references to it.
 */
 
 void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
@@ -232,8 +250,6 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
     ec_eoe_t *eoe, *next_eoe;
     ec_datagram_t *datagram, *next_c;
 
-    ec_master_clear_slaves(master);
-
     // empty datagram queue
     list_for_each_entry_safe(datagram, next_c,
                              &master->datagram_queue, queue) {
@@ -252,7 +268,7 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
         kfree(eoe);
     }
 
-    EC_INFO("Master %i cleared.\n", master->index);
+    EC_INFO("Master %i freed.\n", master->index);
 
     kfree(master);
 }
@@ -260,17 +276,16 @@ void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
 /*****************************************************************************/
 
 /**
-   Clears all slaves.
+   Destroy all slaves.
 */
 
-void ec_master_clear_slaves(ec_master_t *master)
+void ec_master_destroy_slaves(ec_master_t *master)
 {
     ec_slave_t *slave, *next_slave;
 
     list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
         list_del(&slave->list);
-        kobject_del(&slave->kobj);
-        kobject_put(&slave->kobj);
+        ec_slave_destroy(slave);
     }
 
     master->slave_count = 0;
@@ -278,6 +293,22 @@ void ec_master_clear_slaves(ec_master_t *master)
 
 /*****************************************************************************/
 
+/**
+   Destroy all domains.
+*/
+
+void ec_master_destroy_domains(ec_master_t *master)
+{
+    ec_domain_t *domain, *next_d;
+
+    list_for_each_entry_safe(domain, next_d, &master->domains, list) {
+        list_del(&domain->list);
+        ec_domain_destroy(domain);
+    }
+}
+
+/*****************************************************************************/
+
 /**
    Flushes the SDO request queue.
 */
@@ -386,15 +417,8 @@ void ec_master_leave_operation_mode(ec_master_t *master
                                     /**< EtherCAT master */)
 {
     ec_slave_t *slave;
-    ec_domain_t *domain, *next_d;
 
-    // clear domains
-    list_for_each_entry_safe(domain, next_d, &master->domains, list) {
-        ec_domain_dequeue_datagrams(domain);
-        list_del(&domain->list);
-        kobject_del(&domain->kobj);
-        kobject_put(&domain->kobj);
-    }
+    ec_master_destroy_domains(master);
 
     master->request_cb = NULL;
     master->release_cb = NULL;
diff --git a/master/master.h b/master/master.h
index 946a4f04..a27154d5 100644
--- a/master/master.h
+++ b/master/master.h
@@ -148,8 +148,9 @@ struct ec_master
 
 /*****************************************************************************/
 
-// master creation
+// master creation/deletion
 int ec_master_init(ec_master_t *, unsigned int, unsigned int, dev_t);
+void ec_master_destroy(ec_master_t *);
 
 // mode transitions
 int ec_master_enter_idle_mode(ec_master_t *);
@@ -167,7 +168,7 @@ void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
 
 // misc.
 void ec_master_output_stats(ec_master_t *);
-void ec_master_clear_slaves(ec_master_t *);
+void ec_master_destroy_slaves(ec_master_t *);
 void ec_master_calc_addressing(ec_master_t *);
 
 // helper functions
diff --git a/master/module.c b/master/module.c
index b5d5a63f..9f115684 100644
--- a/master/module.c
+++ b/master/module.c
@@ -152,8 +152,7 @@ void __exit ec_cleanup_module(void)
 
     list_for_each_entry_safe(master, next, &ec_masters, list) {
         list_del(&master->list);
-        kobject_del(&master->kobj);
-        kobject_put(&master->kobj); // free master
+        ec_master_destroy(master);
     }
 
     unregister_chrdev_region(device_number, ec_master_count);
diff --git a/master/slave.c b/master/slave.c
index e27d00a6..038f0d60 100644
--- a/master/slave.c
+++ b/master/slave.c
@@ -189,6 +189,34 @@ int ec_slave_init(ec_slave_t *slave, /**< EtherCAT slave */
 
 /**
    Slave destructor.
+   Clears and frees a slave object.
+*/
+
+void ec_slave_destroy(ec_slave_t *slave /**< EtherCAT slave */)
+{
+    ec_sdo_t *sdo, *next_sdo;
+
+    // free all SDOs
+    list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
+        list_del(&sdo->list);
+        ec_sdo_destroy(sdo);
+    }
+
+    // free SDO kobject
+    if (slave->sdo_dictionary_fetched) kobject_del(&slave->sdo_kobj);
+    kobject_put(&slave->sdo_kobj);
+
+    // destroy self
+    kobject_del(&slave->kobj);
+    kobject_put(&slave->kobj);
+}
+
+/*****************************************************************************/
+
+/**
+   Clear and free slave.
+   This method is called by the kobject,
+   once there are no more references to it.
 */
 
 void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
@@ -198,7 +226,6 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
     ec_sii_sync_t *sync, *next_sync;
     ec_sii_pdo_t *pdo, *next_pdo;
     ec_sii_pdo_entry_t *entry, *next_ent;
-    ec_sdo_t *sdo, *next_sdo;
     ec_sdo_data_t *sdodata, *next_sdodata;
 
     slave = container_of(kobj, ec_slave_t, kobj);
@@ -235,17 +262,6 @@ void ec_slave_clear(struct kobject *kobj /**< kobject of the slave */)
     if (slave->sii_order) kfree(slave->sii_order);
     if (slave->sii_name) kfree(slave->sii_name);
 
-    // free all SDOs
-    list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
-        list_del(&sdo->list);
-        kobject_del(&sdo->kobj);
-        kobject_put(&sdo->kobj);
-    }
-
-    // free SDO kobject FIXME
-    if (slave->sdo_dictionary_fetched) kobject_del(&slave->sdo_kobj);
-    kobject_put(&slave->sdo_kobj);
-
     // free all SDO configurations
     list_for_each_entry_safe(sdodata, next_sdodata, &slave->sdo_confs, list) {
         list_del(&sdodata->list);
diff --git a/master/slave.h b/master/slave.h
index 43dab899..58543da6 100644
--- a/master/slave.h
+++ b/master/slave.h
@@ -259,6 +259,7 @@ struct ec_slave
 
 // slave construction/destruction
 int ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
+void ec_slave_destroy(ec_slave_t *);
 
 void ec_slave_reset(ec_slave_t *);
 
-- 
GitLab