+ struct cds_hlist_head *head;
+ struct cds_hlist_node *node;
+ struct loglevel_entry *e;
+ struct session_loglevel *sl;
+ size_t name_len = strlen(name) + 1;
+ uint32_t hash = jhash(name, name_len-1, 0);
+ int found = 0;
+
+ /* loglevel entry */
+ head = &loglevel_table[hash & (LOGLEVEL_TABLE_SIZE - 1)];
+ cds_hlist_for_each_entry(e, node, head, hlist) {
+ if (!strcmp(name, e->name)) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found) {
+ /*
+ * Using zmalloc here to allocate a variable length element. Could
+ * cause some memory fragmentation if overused.
+ */
+ e = zmalloc(sizeof(struct loglevel_entry) + name_len);
+ if (!e)
+ return ERR_PTR(-ENOMEM);
+ memcpy(&e->name[0], name, name_len);
+ cds_hlist_add_head(&e->hlist, head);
+ }
+
+ /* session loglevel */
+ cds_list_for_each_entry(sl, &e->session_list, session_list) {
+ if (chan == sl->chan) {
+ DBG("loglevel %s busy for this channel", name);
+ return ERR_PTR(-EEXIST); /* Already there */
+ }
+ }
+ sl = zmalloc(sizeof(struct session_loglevel));
+ if (!sl)
+ return ERR_PTR(-ENOMEM);
+ sl->chan = chan;
+ sl->enabled = 1;
+ memcpy(&sl->event_param, event_param, sizeof(sl->event_param));
+ CDS_INIT_LIST_HEAD(&sl->events);
+ cds_list_add(&sl->list, &chan->session->loglevels);
+ cds_list_add(&sl->session_list, &e->session_list);
+ _probes_create_loglevel_events(e, sl);
+ return sl;