This hashtable implementation is using hlist buckets to provide a simple
hashtable to prevent it from getting reimplemented all over the kernel.
Signed-off-by: Sasha Levin <redacted>
---
Sorry for the long delay, I was busy with a bunch of personal things.
Changes since v6:
- Use macros that point to internal static inline functions instead of
implementing everything as a macro.
- Rebase on latest -next.
- Resending the enter patch series on request.
- Break early from hash_empty() if found to be non-empty.
- DECLARE_HASHTABLE/DEFINE_HASHTABLE.
include/linux/hashtable.h | 193 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 include/linux/hashtable.h
Switch to using the new hashtable implementation to store user structs.
This reduces the amount of generic unrelated code in kernel/user.c.
Signed-off-by: Sasha Levin <redacted>
---
kernel/user.c | 33 +++++++++++++--------------------
1 file changed, 13 insertions(+), 20 deletions(-)
Switch workqueues to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the workqueues.
Signed-off-by: Sasha Levin <redacted>
---
kernel/workqueue.c | 86 ++++++++++--------------------------------------------
1 file changed, 15 insertions(+), 71 deletions(-)
@@ -82,8 +83,6 @@ enum {NR_WORKER_POOLS=2,/* # worker pools per gcwq */BUSY_WORKER_HASH_ORDER=6,/* 64 pointers */-BUSY_WORKER_HASH_SIZE=1<<BUSY_WORKER_HASH_ORDER,-BUSY_WORKER_HASH_MASK=BUSY_WORKER_HASH_SIZE-1,MAX_IDLE_WORKERS_RATIO=4,/* 1/4 of busy can be idle */IDLE_WORKER_TIMEOUT=300*HZ,/* keep idle ones for 5 mins */
@@ -180,7 +179,7 @@ struct global_cwq {unsignedintflags;/* L: GCWQ_* flags *//* workers are chained either in busy_hash or pool idle_list */-structhlist_headbusy_hash[BUSY_WORKER_HASH_SIZE];+DECLARE_HASHTABLE(busy_hash,BUSY_WORKER_HASH_ORDER);/* L: hash of busy workers */structworker_poolpools[NR_WORKER_POOLS];
@@ -285,8 +284,7 @@ EXPORT_SYMBOL_GPL(system_freezable_wq);(pool)<&(gcwq)->pools[NR_WORKER_POOLS];(pool)++)#define for_each_busy_worker(worker, i, pos, gcwq) \-for(i=0;i<BUSY_WORKER_HASH_SIZE;i++)\-hlist_for_each_entry(worker,pos,&gcwq->busy_hash[i],hentry)+hash_for_each(gcwq->busy_hash,i,pos,worker,hentry)staticinlineint__next_gcwq_cpu(intcpu,conststructcpumask*mask,unsignedintsw)
@@ -857,63 +855,6 @@ static inline void worker_clr_flags(struct worker *worker, unsigned int flags)}/**-*busy_worker_head-returnthebusyhashheadforawork-*@gcwq:gcwqofinterest-*@work:worktobehashed-*-*Returnhashheadof@gcwqfor@work.-*-*CONTEXT:-*spin_lock_irq(gcwq->lock).-*-*RETURNS:-*Pointertothehashhead.-*/-staticstructhlist_head*busy_worker_head(structglobal_cwq*gcwq,-structwork_struct*work)-{-constintbase_shift=ilog2(sizeof(structwork_struct));-unsignedlongv=(unsignedlong)work;--/* simple shift and fold hash, do we need something better? */-v>>=base_shift;-v+=v>>BUSY_WORKER_HASH_ORDER;-v&=BUSY_WORKER_HASH_MASK;--return&gcwq->busy_hash[v];-}--/**-*__find_worker_executing_work-findworkerwhichisexecutingawork-*@gcwq:gcwqofinterest-*@bwh:hashheadasreturnedbybusy_worker_head()-*@work:worktofindworkerfor-*-*Findaworkerwhichisexecuting@workon@gcwq.@bwhshouldbe-*thehashheadobtainedbycallingbusy_worker_head()withthesame-*work.-*-*CONTEXT:-*spin_lock_irq(gcwq->lock).-*-*RETURNS:-*Pointertoworkerwhichisexecuting@workiffound,NULL-*otherwise.-*/-staticstructworker*__find_worker_executing_work(structglobal_cwq*gcwq,-structhlist_head*bwh,-structwork_struct*work)-{-structworker*worker;-structhlist_node*tmp;--hlist_for_each_entry(worker,tmp,bwh,hentry)-if(worker->current_work==work)-returnworker;-returnNULL;-}--/***find_worker_executing_work-findworkerwhichisexecutingawork*@gcwq:gcwqofinterest*@work:worktofindworkerfor
@@ -3823,7 +3769,6 @@ out_unlock:staticint__initinit_workqueues(void){unsignedintcpu;-inti;/* make sure we have enough bits for OFFQ CPU number */BUILD_BUG_ON((1LU<<(BITS_PER_LONG-WORK_OFFQ_CPU_SHIFT))<
@@ -3841,8 +3786,7 @@ static int __init init_workqueues(void)gcwq->cpu=cpu;gcwq->flags|=GCWQ_DISASSOCIATED;-for(i=0;i<BUSY_WORKER_HASH_SIZE;i++)-INIT_HLIST_HEAD(&gcwq->busy_hash[i]);+hash_init(gcwq->busy_hash);for_each_worker_pool(pool,gcwq){pool->gcwq=gcwq;
Switch hugemem to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the hugemem.
This also removes the dymanic allocation of the hash table. The size of the table is
constant so there's no point in paying the price of an extra dereference when accessing
it.
Signed-off-by: Sasha Levin <redacted>
---
mm/huge_memory.c | 55 ++++++++++++++-----------------------------------------
1 file changed, 14 insertions(+), 41 deletions(-)
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch 9p error table to use the new hashtable implementation. This reduces the amount of
generic unrelated code in 9p.
Signed-off-by: Sasha Levin <redacted>
---
net/9p/error.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
@@ -223,13 +222,13 @@ int p9_errstr2errno(char *errstr, int len)interrno;structhlist_node*p;structerrormap*c;-intbucket;+u32hash;errno=0;p=NULL;c=NULL;-bucket=jhash(errstr,len,0)%ERRHASHSZ;-hlist_for_each_entry(c,p,&hash_errmap[bucket],list){+hash=jhash(errstr,len,0);+hash_for_each_possible(hash_errmap,c,p,list,hash){if(c->namelen==len&&!memcmp(c->name,errstr,len)){errno=c->val;break;
Switch elevator to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the elevator.
This also removes the dymanic allocation of the hash table. The size of the table is
constant so there's no point in paying the price of an extra dereference when accessing
it.
Signed-off-by: Sasha Levin <redacted>
---
block/blk.h | 2 +-
block/elevator.c | 23 ++++-------------------
include/linux/elevator.h | 5 ++++-
3 files changed, 9 insertions(+), 21 deletions(-)
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch cache to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the cache implementation.
Signed-off-by: Sasha Levin <redacted>
---
net/sunrpc/cache.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
@@ -1636,6 +1632,8 @@ static int create_cache_proc_entries(struct cache_detail *cd, struct net *net)void__initcache_initialize(void){INIT_DEFERRABLE_WORK(&cache_cleaner,do_cache_clean);++hash_init(cache_defer_hash);}intcache_register_net(structcache_detail*cd,structnet*net)
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -107,8 +108,8 @@ static unsigned int l2tp_net_id;structl2tp_net{structlist_headl2tp_tunnel_list;spinlock_tl2tp_tunnel_list_lock;-structhlist_headl2tp_session_hlist[L2TP_HASH_SIZE_2];-spinlock_tl2tp_session_hlist_lock;+DECLARE_HASHTABLE(l2tp_session_hash,L2TP_HASH_BITS_2);+spinlock_tl2tp_session_hash_lock;};staticvoidl2tp_session_set_header_len(structl2tp_session*session,intversion);
@@ -156,30 +157,17 @@ do { \#define l2tp_tunnel_dec_refcount(t) l2tp_tunnel_dec_refcount_1(t)#endif-/* Session hash global list for L2TPv3.-*Thesession_idSHOULDberandomaccordingtoRFC3931,butseveral-*L2TPimplementationsuseincrementingsession_ids.Sowedoareal-*hashonthesession_id,ratherthanasimplebitmask.-*/-staticinlinestructhlist_head*-l2tp_session_id_hash_2(structl2tp_net*pn,u32session_id)-{-return&pn->l2tp_session_hlist[hash_32(session_id,L2TP_HASH_BITS_2)];--}-/* Lookup a session by id in the global session list*/staticstructl2tp_session*l2tp_session_find_2(structnet*net,u32session_id){structl2tp_net*pn=l2tp_pernet(net);-structhlist_head*session_list=-l2tp_session_id_hash_2(pn,session_id);structl2tp_session*session;structhlist_node*walk;rcu_read_lock_bh();-hlist_for_each_entry_rcu(session,walk,session_list,global_hlist){+hash_for_each_possible_rcu(pn->l2tp_session_hash,session,walk,+global_hlist,session_id){if(session->session_id==session_id){rcu_read_unlock_bh();returnsession;
@@ -190,23 +178,10 @@ static struct l2tp_session *l2tp_session_find_2(struct net *net, u32 session_id)returnNULL;}-/* Session hash list.-*Thesession_idSHOULDberandomaccordingtoRFC2661,butseveral-*L2TPimplementations(CiscoandMicrosoft)useincrementing-*session_ids.Sowedoarealhashonthesession_id,ratherthana-*simplebitmask.-*/-staticinlinestructhlist_head*-l2tp_session_id_hash(structl2tp_tunnel*tunnel,u32session_id)-{-return&tunnel->session_hlist[hash_32(session_id,L2TP_HASH_BITS)];-}-/* Lookup a session by id*/structl2tp_session*l2tp_session_find(structnet*net,structl2tp_tunnel*tunnel,u32session_id){-structhlist_head*session_list;structl2tp_session*session;structhlist_node*walk;
@@ -1282,16 +1252,14 @@ static void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel)l2tp_info(tunnel,L2TP_MSG_CONTROL,"%s: closing all sessions...\n",tunnel->name);-write_lock_bh(&tunnel->hlist_lock);-for(hash=0;hash<L2TP_HASH_SIZE;hash++){-again:-hlist_for_each_safe(walk,tmp,&tunnel->session_hlist[hash]){-session=hlist_entry(walk,structl2tp_session,hlist);-+write_lock_bh(&tunnel->hash_lock);+do{+found=0;+hash_for_each_safe(tunnel->session_hash,hash,walk,tmp,session,hlist){l2tp_info(session,L2TP_MSG_CONTROL,"%s: closing session\n",session->name);-hlist_del_init(&session->hlist);+hash_del(&session->hlist);/* Since we should hold the sock lock while*doinganyunbinding,weneedtoreleasethe
@@ -1319,17 +1287,17 @@ again:if(session->deref!=NULL)(*session->deref)(session);-write_lock_bh(&tunnel->hlist_lock);+write_lock_bh(&tunnel->hash_lock);/* Now restart from the beginning of this hash*chain.Wealwaysremoveasessionfromthe*listsoweareguaranteedtomakeforward*progress.*/-gotoagain;+found=1;}-}-write_unlock_bh(&tunnel->hlist_lock);+}while(found);+write_unlock_bh(&tunnel->hash_lock);}/* Really kill the tunnel.
@@ -1576,7 +1544,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32tunnel->magic=L2TP_TUNNEL_MAGIC;sprintf(&tunnel->name[0],"tunl %u",tunnel_id);-rwlock_init(&tunnel->hlist_lock);+rwlock_init(&tunnel->hash_lock);/* The net we belong to */tunnel->l2tp_net=net;
@@ -1613,6 +1581,8 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32/* Add tunnel to our list */INIT_LIST_HEAD(&tunnel->list);++hash_init(tunnel->session_hash);atomic_inc(&l2tp_tunnel_count);/* Bump the reference count. The tunnel context is deleted
@@ -1677,17 +1647,17 @@ void l2tp_session_free(struct l2tp_session *session)BUG_ON(tunnel->magic!=L2TP_TUNNEL_MAGIC);/* Delete the session from the hash */-write_lock_bh(&tunnel->hlist_lock);-hlist_del_init(&session->hlist);-write_unlock_bh(&tunnel->hlist_lock);+write_lock_bh(&tunnel->hash_lock);+hash_del(&session->hlist);+write_unlock_bh(&tunnel->hash_lock);/* Unlink from the global hash if not L2TPv2 */if(tunnel->version!=L2TP_HDR_VER_2){structl2tp_net*pn=l2tp_pernet(tunnel->l2tp_net);-spin_lock_bh(&pn->l2tp_session_hlist_lock);-hlist_del_init_rcu(&session->global_hlist);-spin_unlock_bh(&pn->l2tp_session_hlist_lock);+spin_lock_bh(&pn->l2tp_session_hash_lock);+hash_del_rcu(&session->global_hlist);+spin_unlock_bh(&pn->l2tp_session_hash_lock);synchronize_rcu();}
@@ -1800,19 +1770,17 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnsock_hold(tunnel->sock);/* Add session to the tunnel's hash list */-write_lock_bh(&tunnel->hlist_lock);-hlist_add_head(&session->hlist,-l2tp_session_id_hash(tunnel,session_id));-write_unlock_bh(&tunnel->hlist_lock);+write_lock_bh(&tunnel->hash_lock);+hash_add(tunnel->session_hash,&session->hlist,session_id);+write_unlock_bh(&tunnel->hash_lock);/* And to the global session list if L2TPv3 */if(tunnel->version!=L2TP_HDR_VER_2){structl2tp_net*pn=l2tp_pernet(tunnel->l2tp_net);-spin_lock_bh(&pn->l2tp_session_hlist_lock);-hlist_add_head_rcu(&session->global_hlist,-l2tp_session_id_hash_2(pn,session_id));-spin_unlock_bh(&pn->l2tp_session_hlist_lock);+spin_lock_bh(&pn->l2tp_session_hash_lock);+hash_add(pn->l2tp_session_hash,&session->global_hlist,session_id);+spin_unlock_bh(&pn->l2tp_session_hash_lock);}/* Ignore management session in session count value */
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch lockd to use the new hashtable implementation. This reduces the amount of
generic unrelated code in lockd.
Signed-off-by: Sasha Levin <redacted>
---
fs/lockd/svcsubs.c | 66 +++++++++++++++++++++++++++++-------------------------
1 file changed, 36 insertions(+), 30 deletions(-)
@@ -253,27 +253,25 @@ nlm_traverse_files(void *data, nlm_host_match_fn_t match,inti,ret=0;mutex_lock(&nlm_file_mutex);-for(i=0;i<FILE_NRHASH;i++){-hlist_for_each_entry_safe(file,pos,next,&nlm_files[i],f_list){-if(is_failover_file&&!is_failover_file(data,file))-continue;-file->f_count++;-mutex_unlock(&nlm_file_mutex);--/* Traverse locks, blocks and shares of this file-*andupdatefile->f_lockscount*/-if(nlm_inspect_file(data,file,match))-ret=1;--mutex_lock(&nlm_file_mutex);-file->f_count--;-/* No more references to this file. Let go of it. */-if(list_empty(&file->f_blocks)&&!file->f_locks-&&!file->f_shares&&!file->f_count){-hlist_del(&file->f_list);-nlmsvc_ops->fclose(file->f_file);-kfree(file);-}+hash_for_each_safe(nlm_files,i,pos,next,file,f_list){+if(is_failover_file&&!is_failover_file(data,file))+continue;+file->f_count++;+mutex_unlock(&nlm_file_mutex);++/* Traverse locks, blocks and shares of this file+*andupdatefile->f_lockscount*/+if(nlm_inspect_file(data,file,match))+ret=1;++mutex_lock(&nlm_file_mutex);+file->f_count--;+/* No more references to this file. Let go of it. */+if(list_empty(&file->f_blocks)&&!file->f_locks+&&!file->f_shares&&!file->f_count){+hash_del(&file->f_list);+nlmsvc_ops->fclose(file->f_file);+kfree(file);}}mutex_unlock(&nlm_file_mutex);
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch openvswitch to use the new hashtable implementation. This reduces the amount of
generic unrelated code in openvswitch.
Signed-off-by: Sasha Levin <redacted>
---
net/openvswitch/vport.c | 34 +++++++++++++---------------------
1 file changed, 13 insertions(+), 21 deletions(-)
Switch tracing to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracing module.
Signed-off-by: Sasha Levin <redacted>
---
kernel/trace/trace_output.c | 20 ++++++++------------
1 file changed, 8 insertions(+), 12 deletions(-)
@@ -8,15 +8,15 @@#include<linux/module.h>#include<linux/mutex.h>#include<linux/ftrace.h>+#include<linux/hashtable.h>#include"trace_output.h"-/* must be a power of 2 */-#define EVENT_HASHSIZE 128+#define EVENT_HASH_BITS 7DECLARE_RWSEM(trace_event_mutex);-staticstructhlist_headevent_hash[EVENT_HASHSIZE]__read_mostly;+staticDEFINE_HASHTABLE(event_hash,EVENT_HASH_BITS);staticintnext_event_type=__TRACE_LAST_TYPE+1;
Switch rds to use the new hashtable implementation. This reduces the amount of
generic unrelated code in rds.
Signed-off-by: Sasha Levin <redacted>
---
net/rds/bind.c | 28 +++++++++-----
net/rds/connection.c | 102 +++++++++++++++++++++++----------------------------
2 files changed, 63 insertions(+), 67 deletions(-)
@@ -34,28 +34,24 @@#include<linux/list.h>#include<linux/slab.h>#include<linux/export.h>+#include<linux/hashtable.h>#include<net/inet_hashtables.h>#include"rds.h"#include"loop.h"#define RDS_CONNECTION_HASH_BITS 12-#define RDS_CONNECTION_HASH_ENTRIES (1 << RDS_CONNECTION_HASH_BITS)-#define RDS_CONNECTION_HASH_MASK (RDS_CONNECTION_HASH_ENTRIES - 1)/* converting this to RCU is a chore for another day.. */staticDEFINE_SPINLOCK(rds_conn_lock);staticunsignedlongrds_conn_count;-staticstructhlist_headrds_conn_hash[RDS_CONNECTION_HASH_ENTRIES];+staticDEFINE_HASHTABLE(rds_conn_hash,RDS_CONNECTION_HASH_BITS);staticstructkmem_cache*rds_conn_slab;-staticstructhlist_head*rds_conn_bucket(__be32laddr,__be32faddr)+staticunsignedlongrds_conn_hashfn(__be32laddr,__be32faddr){/* Pass NULL, don't need struct net for hash */-unsignedlonghash=inet_ehashfn(NULL,-be32_to_cpu(laddr),0,-be32_to_cpu(faddr),0);-return&rds_conn_hash[hash&RDS_CONNECTION_HASH_MASK];+returninet_ehashfn(NULL,be32_to_cpu(laddr),0,be32_to_cpu(faddr),0);}#define rds_conn_info_set(var, test, suffix) do { \
@@ -64,14 +60,14 @@ static struct hlist_head *rds_conn_bucket(__be32 laddr, __be32 faddr)}while(0)/* rcu read lock must be held or the connection spinlock */-staticstructrds_connection*rds_conn_lookup(structhlist_head*head,-__be32laddr,__be32faddr,+staticstructrds_connection*rds_conn_lookup(__be32laddr,__be32faddr,structrds_transport*trans){structrds_connection*conn,*ret=NULL;structhlist_node*pos;+unsignedlongkey=rds_conn_hashfn(laddr,faddr);-hlist_for_each_entry_rcu(conn,pos,head,c_hash_node){+hash_for_each_possible_rcu(rds_conn_hash,conn,pos,c_hash_node,key){if(conn->c_faddr==faddr&&conn->c_laddr==laddr&&conn->c_trans==trans){ret=conn;
@@ -117,13 +113,12 @@ static struct rds_connection *__rds_conn_create(__be32 laddr, __be32 faddr,intis_outgoing){structrds_connection*conn,*parent=NULL;-structhlist_head*head=rds_conn_bucket(laddr,faddr);structrds_transport*loop_trans;unsignedlongflags;intret;rcu_read_lock();-conn=rds_conn_lookup(head,laddr,faddr,trans);+conn=rds_conn_lookup(laddr,faddr,trans);if(conn&&conn->c_loopback&&conn->c_trans!=&rds_loop_transport&&!is_outgoing){/* This is a looped back IB connection, and we're
@@ -329,7 +326,7 @@ void rds_conn_destroy(struct rds_connection *conn)/* Ensure conn will not be scheduled for reconnect */spin_lock_irq(&rds_conn_lock);-hlist_del_init_rcu(&conn->c_hash_node);+hash_del(&conn->c_hash_node);spin_unlock_irq(&rds_conn_lock);synchronize_rcu();
@@ -448,23 +440,19 @@ void rds_for_each_conn_info(struct socket *sock, unsigned int len,lens->nr=0;lens->each=item_len;-for(i=0,head=rds_conn_hash;i<ARRAY_SIZE(rds_conn_hash);-i++,head++){-hlist_for_each_entry_rcu(conn,pos,head,c_hash_node){--/* XXX no c_lock usage.. */-if(!visitor(conn,buffer))-continue;--/* We copy as much as we can fit in the buffer,-*butwecountallitemssothatthecaller-*canresizethebuffer.*/-if(len>=item_len){-rds_info_copy(iter,buffer,item_len);-len-=item_len;-}-lens->nr++;+hash_for_each_rcu(rds_conn_hash,i,pos,conn,c_hash_node){+/* XXX no c_lock usage.. */+if(!visitor(conn,buffer))+continue;++/* We copy as much as we can fit in the buffer,+*butwecountallitemssothatthecaller+*canresizethebuffer.*/+if(len>=item_len){+rds_info_copy(iter,buffer,item_len);+len-=item_len;}+lens->nr++;}rcu_read_unlock();}
@@ -518,6 +506,8 @@ int rds_conn_init(void)rds_info_register_func(RDS_INFO_RETRANS_MESSAGES,rds_conn_message_info_retrans);+hash_init(rds_conn_hash);+return0;}
@@ -80,7 +78,7 @@ struct dm_snapshot {/* Chunks with outstanding reads */spinlock_ttracked_chunk_lock;mempool_t*tracked_chunk_pool;-structhlist_headtracked_chunk_hash[DM_TRACKED_CHUNK_HASH_SIZE];+DECLARE_HASHTABLE(tracked_chunk_hash,DM_TRACKED_CHUNK_HASH_BITS);/* The on disk metadata handler */structdm_exception_store*store;
Switch dlm to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the dlm.
Signed-off-by: Sasha Levin <redacted>
---
fs/dlm/lowcomms.c | 47 +++++++++++++----------------------------------
1 file changed, 13 insertions(+), 34 deletions(-)
@@ -62,7 +63,7 @@#include"config.h"#define NEEDED_RMEM (4*1024*1024)-#define CONN_HASH_SIZE 32+#define CONN_HASH_BITS 5/* Number of messages to send before rescheduling */#define MAX_SEND_MSG_COUNT 25
@@ -158,34 +159,21 @@ static int dlm_allow_conn;staticstructworkqueue_struct*recv_workqueue;staticstructworkqueue_struct*send_workqueue;-staticstructhlist_headconnection_hash[CONN_HASH_SIZE];+staticstructhlist_headconnection_hash[CONN_HASH_BITS];staticDEFINE_MUTEX(connections_lock);staticstructkmem_cache*con_cache;staticvoidprocess_recv_sockets(structwork_struct*work);staticvoidprocess_send_sockets(structwork_struct*work);--/* This is deliberately very simple because most clusters have simple-sequentialnodeids,soweshouldbeabletogostraighttoaconnection-structinthearray*/-staticinlineintnodeid_hash(intnodeid)-{-returnnodeid&(CONN_HASH_SIZE-1);-}-staticstructconnection*__find_con(intnodeid){-intr;structhlist_node*h;structconnection*con;-r=nodeid_hash(nodeid);--hlist_for_each_entry(con,h,&connection_hash[r],list){+hash_for_each_possible(connection_hash,con,h,list,nodeid)if(con->nodeid==nodeid)returncon;-}returnNULL;}
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
--
1.7.12.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch ksm to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the ksm module.
Signed-off-by: Sasha Levin <redacted>
---
mm/ksm.c | 33 +++++++++++++++------------------
1 file changed, 15 insertions(+), 18 deletions(-)
On Sun, Oct 28, 2012 at 03:02:16PM -0400, Sasha Levin wrote:
Switch workqueues to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the workqueues.
Signed-off-by: Sasha Levin <redacted>
On Sun, Oct 28, 2012 at 03:02:20PM -0400, Sasha Levin wrote:
Switch elevator to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the elevator.
This also removes the dymanic allocation of the hash table. The size of the table is
constant so there's no point in paying the price of an extra dereference when accessing
it.
Signed-off-by: Sasha Levin <redacted>
Reviewed-by: Tejun Heo <tj@kernel.orG>
But please reformat commit message to fit inside 80col (preferably 74
or something like that).
Thanks.
--
tejun
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
This hashtable implementation is using hlist buckets to provide a simple
hashtable to prevent it from getting reimplemented all over the kernel.
Signed-off-by: Sasha Levin <redacted>
---
Sorry for the long delay, I was busy with a bunch of personal things.
Changes since v6:
- Use macros that point to internal static inline functions instead of
implementing everything as a macro.
- Rebase on latest -next.
- Resending the enter patch series on request.
- Break early from hash_empty() if found to be non-empty.
- DECLARE_HASHTABLE/DEFINE_HASHTABLE.
include/linux/hashtable.h | 193 ++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 193 insertions(+)
create mode 100644 include/linux/hashtable.h
Although it's unlikely that someone would use this with a binary
operator with lower precedence than "<<" (see e.g.
http://www.swansontec.com/sopc.html) as "bits", lack of parenthesis
around "bits" would be unexpected by the caller, and could introduce
bugs. Please review all macros with the precedence table in mind, and
ask yourself if lack of parenthesis could introduce a subtle bug.
Here, you have parenthesis around "bits", but not above (inconsistency).
quoted hunk
++#define HASH_SIZE(name) (ARRAY_SIZE(name))+#define HASH_BITS(name) ilog2(HASH_SIZE(name))++/* Use hash_32 when possible to allow for fast 32bit hashing in 64bit kernels. */+#define hash_min(val, bits) \+({ \+ sizeof(val) <= 4 ? \+ hash_32(val, bits) : \+ hash_long(val, bits); \+})++static inline void __hash_init(struct hlist_head *ht, int sz)
int -> unsigned int.
+{
+ int i;
int -> unsigned int.
quoted hunk
++ for (i = 0; i < sz; i++)+ INIT_HLIST_HEAD(&ht[sz]);
ouch. How did this work ? Has it been tested at all ?
sz -> i
quoted hunk
+}++/**+ * hash_init - initialize a hash table+ * @hashtable: hashtable to be initialized+ *+ * Calculates the size of the hashtable from the given parameter, otherwise+ * same as hash_init_size.+ *+ * This has to be a macro since HASH_BITS() will not work on pointers since+ * it calculates the size during preprocessing.+ */+#define hash_init(hashtable) __hash_init(hashtable, HASH_SIZE(hashtable))++/**+ * hash_add - add an object to a hashtable+ * @hashtable: hashtable to add to+ * @node: the &struct hlist_node of the object to be added+ * @key: the key of the object to be added+ */+#define hash_add(hashtable, node, key) \+ hlist_add_head(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]);
extra ";" at the end to remove.
quoted hunk
++/**+ * hash_add_rcu - add an object to a rcu enabled hashtable+ * @hashtable: hashtable to add to+ * @node: the &struct hlist_node of the object to be added+ * @key: the key of the object to be added+ */+#define hash_add_rcu(hashtable, node, key) \+ hlist_add_head_rcu(node, &hashtable[hash_min(key, HASH_BITS(hashtable))]);
extra ";" at the end to remove.
quoted hunk
++/**+ * hash_hashed - check whether an object is in any hashtable+ * @node: the &struct hlist_node of the object to be checked+ */+#define hash_hashed(node) (!hlist_unhashed(node))
Please use a static inline for this instead of a macro.
+
+static inline bool __hash_empty(struct hlist_head *ht, int sz)
int -> unsigned int.
+{
+ int i;
int -> unsigned int.
quoted hunk
++ for (i = 0; i < sz; i++)+ if (!hlist_empty(&ht[i]))+ return false;++ return true;+}++/**+ * hash_empty - check whether a hashtable is empty+ * @hashtable: hashtable to check+ *+ * This has to be a macro since HASH_BITS() will not work on pointers since+ * it calculates the size during preprocessing.+ */+#define hash_empty(hashtable) __hash_empty(hashtable, HASH_SIZE(hashtable))++/**+ * hash_del - remove an object from a hashtable+ * @node: &struct hlist_node of the object to remove+ */+static inline void hash_del(struct hlist_node *node)+{+ hlist_del_init(node);+}++/**+ * hash_del_rcu - remove an object from a rcu enabled hashtable+ * @node: &struct hlist_node of the object to remove+ */+static inline void hash_del_rcu(struct hlist_node *node)+{+ hlist_del_init_rcu(node);+}++/**+ * hash_for_each - iterate over a hashtable+ * @name: hashtable to iterate+ * @bkt: integer to use as bucket loop cursor+ * @node: the &struct list_head to use as a loop cursor for each entry+ * @obj: the type * to use as a loop cursor for each entry+ * @member: the name of the hlist_node within the struct+ */+#define hash_for_each(name, bkt, node, obj, member) \+ for (bkt = 0, node = NULL; node == NULL && bkt < HASH_SIZE(name); bkt++)\
if "bkt" happens to be a dereferenced pointer (unary operator '*'), we
get into a situation where "*blah" has higher precedence than "=",
higher than "<", but lower than "++". Any thoughts on fixing this ?
quoted hunk
+ hlist_for_each_entry(obj, node, &name[bkt], member)++/**+ * hash_for_each_rcu - iterate over a rcu enabled hashtable+ * @name: hashtable to iterate+ * @bkt: integer to use as bucket loop cursor+ * @node: the &struct list_head to use as a loop cursor for each entry+ * @obj: the type * to use as a loop cursor for each entry+ * @member: the name of the hlist_node within the struct+ */+#define hash_for_each_rcu(name, bkt, node, obj, member) \+ for (bkt = 0, node = NULL; node == NULL && bkt < HASH_SIZE(name); bkt++)\
Same comment as above about "bkt".
quoted hunk
+ hlist_for_each_entry_rcu(obj, node, &name[bkt], member)++/**+ * hash_for_each_safe - iterate over a hashtable safe against removal of+ * hash entry+ * @name: hashtable to iterate+ * @bkt: integer to use as bucket loop cursor+ * @node: the &struct list_head to use as a loop cursor for each entry+ * @tmp: a &struct used for temporary storage+ * @obj: the type * to use as a loop cursor for each entry+ * @member: the name of the hlist_node within the struct+ */+#define hash_for_each_safe(name, bkt, node, tmp, obj, member) \+ for (bkt = 0, node = NULL; node == NULL && bkt < HASH_SIZE(name); bkt++)\
Same comment as above about "bkt".
Thanks,
Mathieu
+ hlist_for_each_entry_safe(obj, node, tmp, &name[bkt], member)
+
+/**
+ * hash_for_each_possible - iterate over all possible objects hashing to the
+ * same bucket
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @node: the &struct list_head to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible(name, obj, node, member, key) \
+ hlist_for_each_entry(obj, node, &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_rcu - iterate over all possible objects hashing to the
+ * same bucket in an rcu enabled hashtable
+ * in a rcu enabled hashtable
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @node: the &struct list_head to use as a loop cursor for each entry
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_rcu(name, obj, node, member, key) \
+ hlist_for_each_entry_rcu(obj, node, &name[hash_min(key, HASH_BITS(name))], member)
+
+/**
+ * hash_for_each_possible_safe - iterate over all possible objects hashing to the
+ * same bucket safe against removals
+ * @name: hashtable to iterate
+ * @obj: the type * to use as a loop cursor for each entry
+ * @node: the &struct list_head to use as a loop cursor for each entry
+ * @tmp: a &struct used for temporary storage
+ * @member: the name of the hlist_node within the struct
+ * @key: the key of the objects to iterate over
+ */
+#define hash_for_each_possible_safe(name, obj, node, tmp, member, key) \
+ hlist_for_each_entry_safe(obj, node, tmp, \
+ &name[hash_min(key, HASH_BITS(name))], member)
+
+
+#endif
--
1.7.12.4
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch 9p error table to use the new hashtable implementation. This reduces the amount of
generic unrelated code in 9p.
Signed-off-by: Sasha Levin <redacted>
---
net/9p/error.c | 21 ++++++++++-----------
1 file changed, 10 insertions(+), 11 deletions(-)
Hrm, so this is moving "registered" out of the elevator_queue first
cache-line by turning the pointer into a 256 or 512 bytes hash table.
Maybe we should consider moving "registered" before the "hash" field ?
Thanks,
Mathieu
};
--
1.7.12.4
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Switch cache to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the cache implementation.
Signed-off-by: Sasha Levin <redacted>
---
net/sunrpc/cache.c | 20 +++++++++-----------
1 file changed, 9 insertions(+), 11 deletions(-)
If we look at a bit of history, mainly commit:
commit 1117449276bb909b029ed0b9ba13f53e4784db9d
Author: NeilBrown [off-list ref]
Date: Thu Aug 12 17:04:08 2010 +1000
sunrpc/cache: change deferred-request hash table to use hlist.
we'll notice that the only reason why the prior DFR_HASHSIZE was using
(PAGE_SIZE/sizeof(struct list_head))
instead of
(PAGE_SIZE/sizeof(struct hlist_head))
is because it has been forgotten in that commit. The intent there is to
make the hash table array fit the page size.
By defining DFR_HASH_BITS arbitrarily to "9", this indeed fulfills this
purpose on architectures with 4kB page size and 64-bit pointers, but not
on some powerpc configurations, and Tile architectures, which have more
exotic 64kB page size, and of course on the far less exotic 32-bit
pointer architectures.
So defining e.g.:
#include <linux/log2.h>
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(BITS_PER_LONG))
would keep the intended behavior in all cases: use one page for the hash
array.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -158,34 +159,21 @@ static int dlm_allow_conn; static struct workqueue_struct *recv_workqueue; static struct workqueue_struct *send_workqueue;-static struct hlist_head connection_hash[CONN_HASH_SIZE];+static struct hlist_head connection_hash[CONN_HASH_BITS]; static DEFINE_MUTEX(connections_lock); static struct kmem_cache *con_cache; static void process_recv_sockets(struct work_struct *work); static void process_send_sockets(struct work_struct *work);--/* This is deliberately very simple because most clusters have simple- sequential nodeids, so we should be able to go straight to a connection- struct in the array */-static inline int nodeid_hash(int nodeid)-{- return nodeid & (CONN_HASH_SIZE-1);-}
There is one thing I dislike about this change: you remove a useful
comment. It's good to be informed of the reason why a direct mapping
"value -> hash" without any dispersion function is preferred here.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
-/* Session hash global list for L2TPv3.
- * The session_id SHOULD be random according to RFC3931, but several
- * L2TP implementations use incrementing session_ids. So we do a real
- * hash on the session_id, rather than a simple bitmask.
- */
-static inline struct hlist_head *
-l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id)
-{
- return &pn->l2tp_session_hlist[hash_32(session_id, L2TP_HASH_BITS_2)];
-
-}
I understand that you removed this hash function, as well as
"l2tp_session_id_hash" below, but is there any way we could leave those
comments in place ? They look useful.
-/* Session hash list.
- * The session_id SHOULD be random according to RFC2661, but several
- * L2TP implementations (Cisco and Microsoft) use incrementing
- * session_ids. So we do a real hash on the session_id, rather than a
- * simple bitmask.
Ditto.
quoted hunk
- */-static inline struct hlist_head *-l2tp_session_id_hash(struct l2tp_tunnel *tunnel, u32 session_id)-{- return &tunnel->session_hlist[hash_32(session_id, L2TP_HASH_BITS)];-}- /* Lookup a session by id */
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -158,34 +159,21 @@ static int dlm_allow_conn; static struct workqueue_struct *recv_workqueue; static struct workqueue_struct *send_workqueue;-static struct hlist_head connection_hash[CONN_HASH_SIZE];+static struct hlist_head connection_hash[CONN_HASH_BITS]; static DEFINE_MUTEX(connections_lock); static struct kmem_cache *con_cache; static void process_recv_sockets(struct work_struct *work); static void process_send_sockets(struct work_struct *work);--/* This is deliberately very simple because most clusters have simple- sequential nodeids, so we should be able to go straight to a connection- struct in the array */-static inline int nodeid_hash(int nodeid)-{- return nodeid & (CONN_HASH_SIZE-1);-}
There is one thing I dislike about this change: you remove a useful
comment. It's good to be informed of the reason why a direct mapping
"value -> hash" without any dispersion function is preferred here.
And now that I come to think of it: you're changing the behavior : you
will now use a dispersion function on the key, which goes against the
intent expressed in this comment.
It might be good to change hash_add(), hash_add_rcu(),
hash_for_each_possible*() key parameter for a "hash" parameter, and let
the caller provide the hash value computed by the function they like as
parameter, rather than enforcing hash_32/hash_64.
Thoughts ?
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch lockd to use the new hashtable implementation. This reduces the amount of
generic unrelated code in lockd.
Signed-off-by: Sasha Levin <redacted>
---
fs/lockd/svcsubs.c | 66 +++++++++++++++++++++++++++++-------------------------
1 file changed, 36 insertions(+), 30 deletions(-)
we have a nice example of weirdness about key vs hash here:
1) "key" is computed from file_hash(f)
2) file_hash(f) is computed again and again in hash_for_each_possible()
quoted hunk
if (!nfs_compare_fh(&file->f_handle, f))
goto found;
3) then we use "key" as parameter to hash_add.
Moreover, we're adding dispersion to the file_hash() with the hash_32()
called under the hook within hashtable.h. Is it an intended behavior ?
This should at the very least be documented in the changelog.
[...]
quoted hunk
+static int __init nlm_init(void)+{+ hash_init(nlm_files);
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Switch rds to use the new hashtable implementation. This reduces the amount of
generic unrelated code in rds.
Signed-off-by: Sasha Levin <redacted>
---
net/rds/bind.c | 28 +++++++++-----
net/rds/connection.c | 102 +++++++++++++++++++++++----------------------------
2 files changed, 63 insertions(+), 67 deletions(-)
here too, key will be hashed twice:
- once by jhash_2words,
- once by hash_32(),
is this intended ?
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
-static struct hlist_head *hash_bucket(struct net *net, const char *name)-{- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);- return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];-}- /** * ovs_vport_locate - find a port that has already been created *
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 5:42 AM, Mathieu Desnoyers
[off-list ref] wrote:
So defining e.g.:
#include <linux/log2.h>
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(BITS_PER_LONG))
would keep the intended behavior in all cases: use one page for the hash
array.
Well, since that wasn't true before either because of the long-time
bug you point out, clearly the page size isn't all that important. I
think it's more important to have small and simple code, and "9" is
certainly that, compared to playing ilog2 games with not-so-obvious
things.
Because there's no reason to believe that '9' is in any way a worse
random number than something page-shift-related, is there? And getting
away from *previous* overly-complicated size calculations that had
been broken because they were too complicated and random, sounds like
a good idea.
Linus
On Mon, Oct 29, 2012 at 5:42 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
So defining e.g.:
#include <linux/log2.h>
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(BITS_PER_LONG))
would keep the intended behavior in all cases: use one page for the hash
array.
Well, since that wasn't true before either because of the long-time
bug you point out, clearly the page size isn't all that important. I
think it's more important to have small and simple code, and "9" is
certainly that, compared to playing ilog2 games with not-so-obvious
things.
Because there's no reason to believe that '9' is in any way a worse
random number than something page-shift-related, is there? And getting
away from *previous* overly-complicated size calculations that had
been broken because they were too complicated and random, sounds like
a good idea.
Good point. I agree that unless we really care about the precise number
of TLB entries and cache lines used by this hash table, we might want to
stay away from page-size and pointer-size based calculation.
It might not hurt to explain this in the patch changelog though.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 5:42 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
So defining e.g.:
#include <linux/log2.h>
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(BITS_PER_LONG))
would keep the intended behavior in all cases: use one page for the hash
array.
Well, since that wasn't true before either because of the long-time
bug you point out, clearly the page size isn't all that important. I
think it's more important to have small and simple code, and "9" is
certainly that, compared to playing ilog2 games with not-so-obvious
things.
Because there's no reason to believe that '9' is in any way a worse
random number than something page-shift-related, is there? And getting
away from *previous* overly-complicated size calculations that had
been broken because they were too complicated and random, sounds like
a good idea.
Good point. I agree that unless we really care about the precise number
of TLB entries and cache lines used by this hash table, we might want to
stay away from page-size and pointer-size based calculation.
It might not hurt to explain this in the patch changelog though.
I'd also be happy to take that as a separate patch now.
--b.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 5:42 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
So defining e.g.:
#include <linux/log2.h>
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(BITS_PER_LONG))
would keep the intended behavior in all cases: use one page for the hash
array.
Well, since that wasn't true before either because of the long-time
bug you point out, clearly the page size isn't all that important. I
think it's more important to have small and simple code, and "9" is
certainly that, compared to playing ilog2 games with not-so-obvious
things.
Because there's no reason to believe that '9' is in any way a worse
random number than something page-shift-related, is there? And getting
away from *previous* overly-complicated size calculations that had
been broken because they were too complicated and random, sounds like
a good idea.
Good point. I agree that unless we really care about the precise number
of TLB entries and cache lines used by this hash table, we might want to
stay away from page-size and pointer-size based calculation.
It might not hurt to explain this in the patch changelog though.
I'd also be happy to take that as a separate patch now.
FYIW: I've made a nice boo-boo above. It should have been:
#define DFR_HASH_BITS (PAGE_SHIFT - ilog2(sizeof(struct hlist_head)))
Because we happen to have a memory indexed in bytes, not in bits. I
guess this goes a long way proving Linus' point about virtues of trivial
code. ;-)
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
-static struct hlist_head *hash_bucket(struct net *net, const char *name)-{- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);- return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];-}- /** * ovs_vport_locate - find a port that has already been created *
Is applying hash_32() on top of full_name_hash() needed and expected ?
Since this was pointed out in several of the patches, I'll answer it
just once here.
I've intentionally "allowed" double hashing with hash_32 to keep the
code simple.
hash_32() is pretty simple and gcc optimizes it to be almost nothing,
so doing that costs us a multiplication and a shift. On the other
hand, we benefit from keeping our code simple - how would we avoid
doing this double hash? adding a different hashtable function for
strings? or a new function for already hashed keys? I think we benefit
a lot from having to mul/shr instead of adding extra lines of code
here.
Thanks,
Sasha
@@ -158,34 +159,21 @@ static int dlm_allow_conn; static struct workqueue_struct *recv_workqueue; static struct workqueue_struct *send_workqueue;-static struct hlist_head connection_hash[CONN_HASH_SIZE];+static struct hlist_head connection_hash[CONN_HASH_BITS]; static DEFINE_MUTEX(connections_lock); static struct kmem_cache *con_cache; static void process_recv_sockets(struct work_struct *work); static void process_send_sockets(struct work_struct *work);--/* This is deliberately very simple because most clusters have simple- sequential nodeids, so we should be able to go straight to a connection- struct in the array */-static inline int nodeid_hash(int nodeid)-{- return nodeid & (CONN_HASH_SIZE-1);-}
There is one thing I dislike about this change: you remove a useful
comment. It's good to be informed of the reason why a direct mapping
"value -> hash" without any dispersion function is preferred here.
Yes, I've removed the comment because it's no longer true with the patch :)
And now that I come to think of it: you're changing the behavior : you
will now use a dispersion function on the key, which goes against the
intent expressed in this comment.
The comment gave us the information that nodeids are mostly
sequential, we no longer need to rely on that.
It might be good to change hash_add(), hash_add_rcu(),
hash_for_each_possible*() key parameter for a "hash" parameter, and let
the caller provide the hash value computed by the function they like as
parameter, rather than enforcing hash_32/hash_64.
Why? We already proved that hash_32() is more than enough as a hashing
function, why complicate things?
Even doing hash_32() on top of another hash is probably a good idea to
keep things simple.
Thanks,
Sasha
-static struct hlist_head *hash_bucket(struct net *net, const char *name)-{- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);- return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];-}- /** * ovs_vport_locate - find a port that has already been created *
Is applying hash_32() on top of full_name_hash() needed and expected ?
Since this was pointed out in several of the patches, I'll answer it
just once here.
I've intentionally "allowed" double hashing with hash_32 to keep the
code simple.
hash_32() is pretty simple and gcc optimizes it to be almost nothing,
so doing that costs us a multiplication and a shift. On the other
hand, we benefit from keeping our code simple - how would we avoid
doing this double hash? adding a different hashtable function for
strings? or a new function for already hashed keys? I think we benefit
a lot from having to mul/shr instead of adding extra lines of code
here.
This could be done, as I pointed out in another email within this
thread, by changing the "key" argument from add/for_each_possible to an
expected "hash" value, and let the caller invoke hash_32() if they want.
I doubt this would add a significant amount of complexity for users of
this API, but would allow much more flexibility to choose hash
functions.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, Oct 29, 2012 at 7:29 AM, Mathieu Desnoyers
[off-list ref] wrote:
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
++ for (i = 0; i < sz; i++)+ INIT_HLIST_HEAD(&ht[sz]);
ouch. How did this work ? Has it been tested at all ?
sz -> i
Funny enough, it works perfectly. Generally as a test I boot the
kernel in a VM and let it fuzz with trinity for a bit, doing that with
the code above worked flawlessly.
While it works, it's obviously wrong. Why does it work though? Usually
there's a list op happening pretty soon after that which brings the
list into proper state.
I've been playing with a patch that adds a magic value into list_head
if CONFIG_DEBUG_LIST is set, and checks that magic in the list debug
code in lib/list_debug.c.
Does it sound like something useful? If so I'll send that patch out.
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
@@ -158,34 +159,21 @@ static int dlm_allow_conn; static struct workqueue_struct *recv_workqueue; static struct workqueue_struct *send_workqueue;-static struct hlist_head connection_hash[CONN_HASH_SIZE];+static struct hlist_head connection_hash[CONN_HASH_BITS]; static DEFINE_MUTEX(connections_lock); static struct kmem_cache *con_cache; static void process_recv_sockets(struct work_struct *work); static void process_send_sockets(struct work_struct *work);--/* This is deliberately very simple because most clusters have simple- sequential nodeids, so we should be able to go straight to a connection- struct in the array */-static inline int nodeid_hash(int nodeid)-{- return nodeid & (CONN_HASH_SIZE-1);-}
There is one thing I dislike about this change: you remove a useful
comment. It's good to be informed of the reason why a direct mapping
"value -> hash" without any dispersion function is preferred here.
Yes, I've removed the comment because it's no longer true with the patch :)
quoted
And now that I come to think of it: you're changing the behavior : you
will now use a dispersion function on the key, which goes against the
intent expressed in this comment.
The comment gave us the information that nodeids are mostly
sequential, we no longer need to rely on that.
I'm fine with turning a direct + modulo mapping into a dispersed hash as
long as there are no underlying assumptions about sequentiality of value
accesses.
If the access pattern would happen to be typically sequential, then
adding dispersion could hurt performances significantly, turning a
frequent L1 access into a L2 access for instance.
quoted
It might be good to change hash_add(), hash_add_rcu(),
hash_for_each_possible*() key parameter for a "hash" parameter, and let
the caller provide the hash value computed by the function they like as
parameter, rather than enforcing hash_32/hash_64.
Why? We already proved that hash_32() is more than enough as a hashing
function, why complicate things?
Even doing hash_32() on top of another hash is probably a good idea to
keep things simple.
All I'm asking is: have you made sure that this hash table is not
deliberately kept sequential (without dispersion) to accelerate specific
access patterns ? This should at least be documented in the changelog.
Thanks,
Mathieu
Thanks,
Sasha
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 7:29 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
++ for (i = 0; i < sz; i++)+ INIT_HLIST_HEAD(&ht[sz]);
ouch. How did this work ? Has it been tested at all ?
sz -> i
Funny enough, it works perfectly. Generally as a test I boot the
kernel in a VM and let it fuzz with trinity for a bit, doing that with
the code above worked flawlessly.
While it works, it's obviously wrong. Why does it work though? Usually
there's a list op happening pretty soon after that which brings the
list into proper state.
I've been playing with a patch that adds a magic value into list_head
if CONFIG_DEBUG_LIST is set, and checks that magic in the list debug
code in lib/list_debug.c.
Does it sound like something useful? If so I'll send that patch out.
Most of the calls to this initialization function apply it on zeroed
memory (static/kzalloc'd...), which makes it useless. I'd actually be in
favor of removing those redundant calls (as I pointed out in another
email), and document that zeroed memory don't need to be explicitly
initialized.
Those sites that need to really reinitialize memory, or initialize it
(if located on the stack or in non-zeroed dynamically allocated memory)
could use a memset to 0, which will likely be faster than setting to
NULL on many architectures.
About testing, I'd recommend taking the few sites that still need the
initialization function, and just initialize the array with garbage
before calling the initialization function. Things should blow up quite
quickly. Doing it as a one-off thing might be enough to catch any issue.
I don't think we need extra magic numbers to catch issues in this rather
obvious init function.
Thanks,
Mathieu
Thanks,
Sasha
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Hello,
On Mon, Oct 29, 2012 at 12:14:12PM -0400, Mathieu Desnoyers wrote:
Most of the calls to this initialization function apply it on zeroed
memory (static/kzalloc'd...), which makes it useless. I'd actually be in
favor of removing those redundant calls (as I pointed out in another
email), and document that zeroed memory don't need to be explicitly
initialized.
Those sites that need to really reinitialize memory, or initialize it
(if located on the stack or in non-zeroed dynamically allocated memory)
could use a memset to 0, which will likely be faster than setting to
NULL on many architectures.
I don't think it's a good idea to optimize out the basic encapsulation
there. We're talking about re-zeroing some static memory areas which
are pretty small. It's just not worth optimizing out at the cost of
proper initializtion. e.g. We might add debug fields to list_head
later.
Thanks.
--
tejun
Hello,
On Mon, Oct 29, 2012 at 12:14:12PM -0400, Mathieu Desnoyers wrote:
quoted
Most of the calls to this initialization function apply it on zeroed
memory (static/kzalloc'd...), which makes it useless. I'd actually be in
favor of removing those redundant calls (as I pointed out in another
email), and document that zeroed memory don't need to be explicitly
initialized.
Those sites that need to really reinitialize memory, or initialize it
(if located on the stack or in non-zeroed dynamically allocated memory)
could use a memset to 0, which will likely be faster than setting to
NULL on many architectures.
I don't think it's a good idea to optimize out the basic encapsulation
there. We're talking about re-zeroing some static memory areas which
are pretty small. It's just not worth optimizing out at the cost of
proper initializtion. e.g. We might add debug fields to list_head
later.
Future-proofness for debugging fields is indeed a very compelling
argument. Fair enough!
We might want to document this intent at the top of the initialization
function though, just in case anyone want to short-circuit it.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: David Teigland <teigland@redhat.com> Date: 2012-10-29 16:24:46
On Mon, Oct 29, 2012 at 12:07:10PM -0400, Mathieu Desnoyers wrote:
I'm fine with turning a direct + modulo mapping into a dispersed hash as
long as there are no underlying assumptions about sequentiality of value
accesses.
If the access pattern would happen to be typically sequential, then
adding dispersion could hurt performances significantly, turning a
frequent L1 access into a L2 access for instance.
All I'm asking is: have you made sure that this hash table is not
deliberately kept sequential (without dispersion) to accelerate specific
access patterns ? This should at least be documented in the changelog.
It was not intentional. I don't expect any benefit would be lost by
making it non-sequential.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 12:14 PM, Mathieu Desnoyers
[off-list ref] wrote:
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
On Mon, Oct 29, 2012 at 7:29 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
++ for (i = 0; i < sz; i++)+ INIT_HLIST_HEAD(&ht[sz]);
ouch. How did this work ? Has it been tested at all ?
sz -> i
Funny enough, it works perfectly. Generally as a test I boot the
kernel in a VM and let it fuzz with trinity for a bit, doing that with
the code above worked flawlessly.
While it works, it's obviously wrong. Why does it work though? Usually
there's a list op happening pretty soon after that which brings the
list into proper state.
I've been playing with a patch that adds a magic value into list_head
if CONFIG_DEBUG_LIST is set, and checks that magic in the list debug
code in lib/list_debug.c.
Does it sound like something useful? If so I'll send that patch out.
Most of the calls to this initialization function apply it on zeroed
memory (static/kzalloc'd...), which makes it useless. I'd actually be in
favor of removing those redundant calls (as I pointed out in another
email), and document that zeroed memory don't need to be explicitly
initialized.
Why would that make it useless? The idea is that the init functions
will set the magic field to something random, like:
.magic = 0xBADBEEF0;
And have list_add() and friends WARN(.magic != 0xBADBEEF0, "Using an
uninitialized list\n");
This way we'll catch all places that don't go through list initialization code.
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
From: Andrew Morton <akpm@linux-foundation.org> Date: 2012-10-29 16:28:16
On Mon, 29 Oct 2012 07:49:42 -0700 Linus Torvalds [off-list ref] wrote:
Because there's no reason to believe that '9' is in any way a worse
random number than something page-shift-related, is there?
9 is much better than PAGE_SHIFT. PAGE_SIZE can vary by a factor of
16, depending on config.
Everyone thinks 4k, and tests only for that. There's potential for
very large performance and behavior changes when their code gets run
on a 64k PAGE_SIZE machine.
++ for (i = 0; i < sz; i++)+ INIT_HLIST_HEAD(&ht[sz]);
ouch. How did this work ? Has it been tested at all ?
sz -> i
Funny enough, it works perfectly. Generally as a test I boot the
kernel in a VM and let it fuzz with trinity for a bit, doing that with
the code above worked flawlessly.
While it works, it's obviously wrong. Why does it work though? Usually
there's a list op happening pretty soon after that which brings the
list into proper state.
I've been playing with a patch that adds a magic value into list_head
if CONFIG_DEBUG_LIST is set, and checks that magic in the list debug
code in lib/list_debug.c.
Does it sound like something useful? If so I'll send that patch out.
Most of the calls to this initialization function apply it on zeroed
memory (static/kzalloc'd...), which makes it useless. I'd actually be in
favor of removing those redundant calls (as I pointed out in another
email), and document that zeroed memory don't need to be explicitly
initialized.
Why would that make it useless? The idea is that the init functions
will set the magic field to something random, like:
.magic = 0xBADBEEF0;
And have list_add() and friends WARN(.magic != 0xBADBEEF0, "Using an
uninitialized list\n");
This way we'll catch all places that don't go through list initialization code.
As I replied to Tejun Heo already, I agree that keeping the
initialization in place makes sense for future-proofness. This intent
should probably be documented in a comment about the initialization
function though, just to make sure nobody will try to skip it.
Thanks,
Mathieu
Thanks,
Sasha
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
On Mon, Oct 29, 2012 at 7:35 AM, Mathieu Desnoyers
[off-list ref] wrote:
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
-static struct hlist_head *hash_bucket(struct net *net, const char *name)-{- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);- return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];-}- /** * ovs_vport_locate - find a port that has already been created *
Is applying hash_32() on top of full_name_hash() needed and expected ?
Since this was pointed out in several of the patches, I'll answer it
just once here.
I've intentionally "allowed" double hashing with hash_32 to keep the
code simple.
hash_32() is pretty simple and gcc optimizes it to be almost nothing,
so doing that costs us a multiplication and a shift. On the other
hand, we benefit from keeping our code simple - how would we avoid
doing this double hash? adding a different hashtable function for
strings? or a new function for already hashed keys? I think we benefit
a lot from having to mul/shr instead of adding extra lines of code
here.
This could be done, as I pointed out in another email within this
thread, by changing the "key" argument from add/for_each_possible to an
expected "hash" value, and let the caller invoke hash_32() if they want.
I doubt this would add a significant amount of complexity for users of
this API, but would allow much more flexibility to choose hash
functions.
Most callers do need to do the hashing though, so why add an
additional step for all callers instead of doing another hash_32 for
the ones that don't really need it?
Another question is why do you need flexibility? I think that
simplicity wins over flexibility here.
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
Agreed,
Thanks,
Mathieu
It's cheap enough and happens only once, so why not?
Thanks,
Sasha
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
-static struct hlist_head *hash_bucket(struct net *net, const char *name)-{- unsigned int hash = jhash(name, strlen(name), (unsigned long) net);- return &dev_table[hash & (VPORT_HASH_BUCKETS - 1)];-}- /** * ovs_vport_locate - find a port that has already been created *
Is applying hash_32() on top of full_name_hash() needed and expected ?
Since this was pointed out in several of the patches, I'll answer it
just once here.
I've intentionally "allowed" double hashing with hash_32 to keep the
code simple.
hash_32() is pretty simple and gcc optimizes it to be almost nothing,
so doing that costs us a multiplication and a shift. On the other
hand, we benefit from keeping our code simple - how would we avoid
doing this double hash? adding a different hashtable function for
strings? or a new function for already hashed keys? I think we benefit
a lot from having to mul/shr instead of adding extra lines of code
here.
This could be done, as I pointed out in another email within this
thread, by changing the "key" argument from add/for_each_possible to an
expected "hash" value, and let the caller invoke hash_32() if they want.
I doubt this would add a significant amount of complexity for users of
this API, but would allow much more flexibility to choose hash
functions.
Most callers do need to do the hashing though, so why add an
additional step for all callers instead of doing another hash_32 for
the ones that don't really need it?
Another question is why do you need flexibility? I think that
simplicity wins over flexibility here.
I usually try to make things as simple as possible, but not simplistic
compared to the problem tackled. In this case, I would ask the following
question: by standardizing the hash function of all those pieces of
kernel infrastructure to "hash_32()", including submodules part of the
kernel network infrastructure, parts of the kernel that can be fed
values coming from user-space (through the VFS), how can you guarantee
that hash_32() won't be the cause of a DoS attack based on the fact that
this algorithm is a) known by an attacker, and b) does not have any
randomness. It's been a recent trend to perform DoS attacks on poorly
implemented hashing functions.
This is just one example in an attempt to show why different hash table
users may have different constraints: for a hash table entirely
populated by keys generated internally by the kernel, a random seed
might not be required, but for cases where values are fed by user-space
and from the NIC, I would argue that flexibility to implement a
randomizable hash function beats implementation simplicity any time.
And you could keep the basic use-case simple by providing hints to the
hash_32()/hash_64()/hash_ulong() helpers in comments.
Thoughts ?
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
Hello,
On Mon, Oct 29, 2012 at 02:16:48PM -0400, Mathieu Desnoyers wrote:
This is just one example in an attempt to show why different hash table
users may have different constraints: for a hash table entirely
populated by keys generated internally by the kernel, a random seed
might not be required, but for cases where values are fed by user-space
and from the NIC, I would argue that flexibility to implement a
randomizable hash function beats implementation simplicity any time.
And you could keep the basic use-case simple by providing hints to the
hash_32()/hash_64()/hash_ulong() helpers in comments.
If all you need is throwing in a salt value to avoid attacks, can't
you just do that from caller side? Scrambling the key before feeding
it into hash_*() should work, no?
Thanks.
--
tejun
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Unnecessary work adds up. Better not to do it unnecessarily, even if by
itself it doesn't cost that much.
It doesn't seem that difficult for future fields to have 0 as their
initialized state.
- Josh Triplett
Hello,
On Mon, Oct 29, 2012 at 02:16:48PM -0400, Mathieu Desnoyers wrote:
quoted
This is just one example in an attempt to show why different hash table
users may have different constraints: for a hash table entirely
populated by keys generated internally by the kernel, a random seed
might not be required, but for cases where values are fed by user-space
and from the NIC, I would argue that flexibility to implement a
randomizable hash function beats implementation simplicity any time.
And you could keep the basic use-case simple by providing hints to the
hash_32()/hash_64()/hash_ulong() helpers in comments.
If all you need is throwing in a salt value to avoid attacks, can't
you just do that from caller side? Scrambling the key before feeding
it into hash_*() should work, no?
Yes, I think salting the "key" parameter would work.
Thanks,
Mathieu
Thanks.
--
tejun
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
On Mon, Oct 29, 2012 at 2:31 PM, Josh Triplett [off-list ref] wrote:
On Mon, Oct 29, 2012 at 01:29:24PM -0400, Sasha Levin wrote:
quoted
On Mon, Oct 29, 2012 at 7:35 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Unnecessary work adds up. Better not to do it unnecessarily, even if by
itself it doesn't cost that much.
It doesn't seem that difficult for future fields to have 0 as their
initialized state.
Let's put it this way: hlist requires the user to initialize hlist
head before usage, therefore as a hlist user, hashtable implementation
must do that.
We do it automatically when the hashtable user does
DEFINE_HASHTABLE(), but we can't do that if he does
DECLARE_HASHTABLE(). This means that the hashtable user must call
hash_init() whenever he uses DECLARE_HASHTABLE() to create his
hashtable.
There are two options here, either we specify that hash_init() should
only be called if DECLARE_HASHTABLE() was called, which is confusing,
inconsistent and prone to errors, or we can just say that it should be
called whenever a hashtable is used.
The only way to work around it IMO is to get hlist to not require
initializing before usage, and there are good reasons that that won't
happen.
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Unnecessary work adds up. Better not to do it unnecessarily, even if by
itself it doesn't cost that much.
It doesn't seem that difficult for future fields to have 0 as their
initialized state.
Let's put it this way: hlist requires the user to initialize hlist
head before usage, therefore as a hlist user, hashtable implementation
must do that.
We do it automatically when the hashtable user does
DEFINE_HASHTABLE(), but we can't do that if he does
DECLARE_HASHTABLE(). This means that the hashtable user must call
hash_init() whenever he uses DECLARE_HASHTABLE() to create his
hashtable.
There are two options here, either we specify that hash_init() should
only be called if DECLARE_HASHTABLE() was called, which is confusing,
inconsistent and prone to errors, or we can just say that it should be
called whenever a hashtable is used.
The only way to work around it IMO is to get hlist to not require
initializing before usage, and there are good reasons that that won't
happen.
Hrm, just a second here.
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
So I take my "Agreed" back. I disagree with initializing the hash table
twice redundantly. There should be at least "DEFINE_HASHTABLE()" or a
hash_init() (for DECLARE_HASHTABLE()), but not useless execution
initialization on top of an already statically initialized hash table.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
On Mon, Oct 29, 2012 at 02:53:19PM -0400, Mathieu Desnoyers wrote:
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
You can do that with [0 .. HASH_SIZE - 1] initializer.
Thanks.
--
tejun
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 11:58:14AM -0700, Tejun Heo wrote:
On Mon, Oct 29, 2012 at 02:53:19PM -0400, Mathieu Desnoyers wrote:
quoted
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
You can do that with [0 .. HASH_SIZE - 1] initializer.
And in general, let's please try not to do optimizations which are
pointless. Just stick to the usual semantics. You have an abstract
data structure - invoke the initializer before using it. Sure,
optimize it if it shows up somewhere. And here, if we do the
initializers properly, it shouldn't cause any more actual overhead -
ie. DEFINE_HASHTABLE() will basicallly boil down to all zero
assignments and the compiler will put the whole thing in .bss anyway.
Thanks.
--
tejun
On Mon, Oct 29, 2012 at 2:53 PM, Mathieu Desnoyers
[off-list ref] wrote:
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
On Mon, Oct 29, 2012 at 2:31 PM, Josh Triplett [off-list ref] wrote:
quoted
On Mon, Oct 29, 2012 at 01:29:24PM -0400, Sasha Levin wrote:
quoted
On Mon, Oct 29, 2012 at 7:35 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Unnecessary work adds up. Better not to do it unnecessarily, even if by
itself it doesn't cost that much.
It doesn't seem that difficult for future fields to have 0 as their
initialized state.
Let's put it this way: hlist requires the user to initialize hlist
head before usage, therefore as a hlist user, hashtable implementation
must do that.
We do it automatically when the hashtable user does
DEFINE_HASHTABLE(), but we can't do that if he does
DECLARE_HASHTABLE(). This means that the hashtable user must call
hash_init() whenever he uses DECLARE_HASHTABLE() to create his
hashtable.
There are two options here, either we specify that hash_init() should
only be called if DECLARE_HASHTABLE() was called, which is confusing,
inconsistent and prone to errors, or we can just say that it should be
called whenever a hashtable is used.
The only way to work around it IMO is to get hlist to not require
initializing before usage, and there are good reasons that that won't
happen.
Hrm, just a second here.
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
So I take my "Agreed" back. I disagree with initializing the hash table
twice redundantly. There should be at least "DEFINE_HASHTABLE()" or a
hash_init() (for DECLARE_HASHTABLE()), but not useless execution
initialization on top of an already statically initialized hash table.
The "magic values" argument was used to point out that some sort of
initialization *must* occur, either by hash_init() or by a proper
initialization in DEFINE_HASHTABLE(), and we can't simply memset() it
to 0. It appears that we all agree on that.
The other thing is whether hash_init() should be called for hashtables
that were created with DEFINE_HASHTABLE(). That point was raised by
Neil Brown last time this series went around, and it seems that no one
objected to the point that it should be consistent across the code.
Even if we ignore hash_init() being mostly optimized out, is it really
worth it taking the risk that some future patch would move a hashtable
that user DEFINE_HASHTABLE() into a struct and will start using
DECLARE_HASHTABLE() and forgetting to initialize it, for example?
Thanks,
Sasha
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 11:58:14AM -0700, Tejun Heo wrote:
quoted
On Mon, Oct 29, 2012 at 02:53:19PM -0400, Mathieu Desnoyers wrote:
quoted
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
You can do that with [0 .. HASH_SIZE - 1] initializer.
And in general, let's please try not to do optimizations which are
pointless. Just stick to the usual semantics. You have an abstract
data structure - invoke the initializer before using it. Sure,
optimize it if it shows up somewhere. And here, if we do the
initializers properly, it shouldn't cause any more actual overhead -
ie. DEFINE_HASHTABLE() will basicallly boil down to all zero
assignments and the compiler will put the whole thing in .bss anyway.
Yes, agreed. I was going too far in optimization land by proposing
assumptions on zeroed memory. All I actually really care about is that
we don't end up calling hash_init() on a statically defined (and thus
already initialized) hash table.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
On Mon, Oct 29, 2012 at 03:09:36PM -0400, Sasha Levin wrote:
The other thing is whether hash_init() should be called for hashtables
that were created with DEFINE_HASHTABLE(). That point was raised by
Neil Brown last time this series went around, and it seems that no one
objected to the point that it should be consistent across the code.
Hmmm? If something is DEFINE_XXX()'d, you definitely shouldn't be
calling XXX_init() on it. That's how it is with most other abstract
data types and you need *VERY* strong rationale to deviate from that.
Thanks.
--
tejun
On Mon, Oct 29, 2012 at 2:53 PM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
On Mon, Oct 29, 2012 at 2:31 PM, Josh Triplett [off-list ref] wrote:
quoted
On Mon, Oct 29, 2012 at 01:29:24PM -0400, Sasha Levin wrote:
quoted
On Mon, Oct 29, 2012 at 7:35 AM, Mathieu Desnoyers
[off-list ref] wrote:
quoted
* Sasha Levin (levinsasha928@gmail.com) wrote:
quoted
Switch tracepoints to use the new hashtable implementation. This reduces the amount of
generic unrelated code in the tracepoints.
Signed-off-by: Sasha Levin <redacted>
---
kernel/tracepoint.c | 27 +++++++++++----------------
1 file changed, 11 insertions(+), 16 deletions(-)
So we have a hash table defined in .bss (therefore entirely initialized
to NULL), and you add a call to "hash_init", which iterates on the whole
array and initialize it to NULL (again) ?
This extra initialization is redundant. I think it should be removed
from here, and hashtable.h should document that hash_init() don't need
to be called on zeroed memory (which includes static/global variables,
kzalloc'd memory, etc).
This was discussed in the previous series, the conclusion was to call
hash_init() either way to keep the encapsulation and consistency.
It's cheap enough and happens only once, so why not?
Unnecessary work adds up. Better not to do it unnecessarily, even if by
itself it doesn't cost that much.
It doesn't seem that difficult for future fields to have 0 as their
initialized state.
Let's put it this way: hlist requires the user to initialize hlist
head before usage, therefore as a hlist user, hashtable implementation
must do that.
We do it automatically when the hashtable user does
DEFINE_HASHTABLE(), but we can't do that if he does
DECLARE_HASHTABLE(). This means that the hashtable user must call
hash_init() whenever he uses DECLARE_HASHTABLE() to create his
hashtable.
There are two options here, either we specify that hash_init() should
only be called if DECLARE_HASHTABLE() was called, which is confusing,
inconsistent and prone to errors, or we can just say that it should be
called whenever a hashtable is used.
The only way to work around it IMO is to get hlist to not require
initializing before usage, and there are good reasons that that won't
happen.
Hrm, just a second here.
The argument about hash_init being useful to add magic values in the
future only works for the cases where a hash table is declared with
DECLARE_HASHTABLE(). It's completely pointless with DEFINE_HASHTABLE(),
because we could initialize any debugging variables from within
DEFINE_HASHTABLE().
So I take my "Agreed" back. I disagree with initializing the hash table
twice redundantly. There should be at least "DEFINE_HASHTABLE()" or a
hash_init() (for DECLARE_HASHTABLE()), but not useless execution
initialization on top of an already statically initialized hash table.
The "magic values" argument was used to point out that some sort of
initialization *must* occur, either by hash_init() or by a proper
initialization in DEFINE_HASHTABLE(), and we can't simply memset() it
to 0. It appears that we all agree on that.
Yes.
The other thing is whether hash_init() should be called for hashtables
that were created with DEFINE_HASHTABLE(). That point was raised by
Neil Brown last time this series went around, and it seems that no one
objected to the point that it should be consistent across the code.
I was probably busy in the San Diego area at that time, or preparing for
it, sorry! :)
Even if we ignore hash_init() being mostly optimized out, is it really
worth it taking the risk that some future patch would move a hashtable
that user DEFINE_HASHTABLE() into a struct and will start using
DECLARE_HASHTABLE() and forgetting to initialize it, for example?
There is a saying that with "if"s, we could put Paris in a bottle. ;)
Please have a look at "linux/wait.h", where if a wait queue is defined
with DEFINE_*(), there is just no need to initialize it at runtime.
There are plenty other kernel headers that do the same. I don't see why
hashtable.h should be different.
Thanks,
Mathieu
--
Mathieu Desnoyers
Operating System Efficiency R&D Consultant
EfficiOS Inc.
http://www.efficios.com
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
On Mon, Oct 29, 2012 at 3:12 PM, Tejun Heo [off-list ref] wrote:
On Mon, Oct 29, 2012 at 03:09:36PM -0400, Sasha Levin wrote:
quoted
The other thing is whether hash_init() should be called for hashtables
that were created with DEFINE_HASHTABLE(). That point was raised by
Neil Brown last time this series went around, and it seems that no one
objected to the point that it should be consistent across the code.
Hmmm? If something is DEFINE_XXX()'d, you definitely shouldn't be
calling XXX_init() on it. That's how it is with most other abstract
data types and you need *VERY* strong rationale to deviate from that.
Neil Brown raised that point last time that this series went around,
and suggested that this should be consistent and hash_init() would
appear everywhere, even if DEFINE_HASHTABLE() was used. Since no one
objected to that I thought we're going with that.
I'll chalk it up to me getting confused :)
Thanks,
Sasha
--
To unsubscribe from this list: send the line "unsubscribe linux-nfs" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html