Free Electrons

Embedded Linux Experts

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
/*
 * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#ifndef __NETFS_H
#define __NETFS_H

#include <linux/types.h>
#include <linux/connector.h>
#include <linux/backing-dev.h>

#define POHMELFS_CN_IDX			5
#define POHMELFS_CN_VAL			0

#define POHMELFS_CTLINFO_ACK		1
#define POHMELFS_NOINFO_ACK		2

#define POHMELFS_NULL_IDX		65535

/*
 * Network command structure.
 * Will be extended.
 */
struct netfs_cmd {
	__u16			cmd;	/* Command number */
	__u16			csize;	/* Attached crypto information size */
	__u16			cpad;	/* Attached padding size */
	__u16			ext;	/* External flags */
	__u32			size;	/* Size of the attached data */
	__u32			trans;	/* Transaction id */
	__u64			id;	/* Object ID to operate on. Used for feedback.*/
	__u64			start;	/* Start of the object. */
	__u64			iv;	/* IV sequence */
	__u8			data[0];
};

static inline void netfs_convert_cmd(struct netfs_cmd *cmd)
{
	cmd->id = __be64_to_cpu(cmd->id);
	cmd->start = __be64_to_cpu(cmd->start);
	cmd->iv = __be64_to_cpu(cmd->iv);
	cmd->cmd = __be16_to_cpu(cmd->cmd);
	cmd->ext = __be16_to_cpu(cmd->ext);
	cmd->csize = __be16_to_cpu(cmd->csize);
	cmd->cpad = __be16_to_cpu(cmd->cpad);
	cmd->size = __be32_to_cpu(cmd->size);
}

#define NETFS_TRANS_SINGLE_DST		(1<<0)

enum {
	NETFS_READDIR	= 1,	/* Read directory for given inode number */
	NETFS_READ_PAGE,	/* Read data page from the server */
	NETFS_WRITE_PAGE,	/* Write data page to the server */
	NETFS_CREATE,		/* Create directory entry */
	NETFS_REMOVE,		/* Remove directory entry */

	NETFS_LOOKUP,		/* Lookup single object */
	NETFS_LINK,		/* Create a link */
	NETFS_TRANS,		/* Transaction */
	NETFS_OPEN,		/* Open intent */
	NETFS_INODE_INFO,	/* Metadata cache coherency synchronization message */

	NETFS_PAGE_CACHE,	/* Page cache invalidation message */
	NETFS_READ_PAGES,	/* Read multiple contiguous pages in one go */
	NETFS_RENAME,		/* Rename object */
	NETFS_CAPABILITIES,	/* Capabilities of the client, for example supported crypto */
	NETFS_LOCK,		/* Distributed lock message */

	NETFS_XATTR_SET,	/* Set extended attribute */
	NETFS_XATTR_GET,	/* Get extended attribute */
	NETFS_CMD_MAX
};

enum {
	POHMELFS_FLAGS_ADD = 0, /* Network state control message for ADD */
	POHMELFS_FLAGS_DEL,     /* Network state control message for DEL */
	POHMELFS_FLAGS_SHOW,    /* Network state control message for SHOW */
	POHMELFS_FLAGS_CRYPTO,	/* Crypto data control message */
	POHMELFS_FLAGS_MODIFY,	/* Network state modification message */
	POHMELFS_FLAGS_DUMP,	/* Network state control message for SHOW ALL */
	POHMELFS_FLAGS_FLUSH,	/* Network state control message for FLUSH */
};

/*
 * Always wanted to copy it from socket headers into public one,
 * since they are __KERNEL__ protected there.
 */
#define _K_SS_MAXSIZE	128

struct saddr {
	unsigned short		sa_family;
	char			addr[_K_SS_MAXSIZE];
};

enum {
	POHMELFS_CRYPTO_HASH = 0,
	POHMELFS_CRYPTO_CIPHER,
};

struct pohmelfs_crypto {
	unsigned int		idx;		/* Config index */
	unsigned short		strlen;		/* Size of the attached crypto string including 0-byte
						 * "cbc(aes)" for example */
	unsigned short		type;		/* HMAC, cipher, both */
	unsigned int		keysize;	/* Key size */
	unsigned char		data[0];	/* Algorithm string, key and IV */
};

#define POHMELFS_IO_PERM_READ		(1<<0)
#define POHMELFS_IO_PERM_WRITE		(1<<1)

/*
 * Configuration command used to create table of different remote servers.
 */
struct pohmelfs_ctl {
	__u32			idx;		/* Config index */
	__u32			type;		/* Socket type */
	__u32			proto;		/* Socket protocol */
	__u16			addrlen;	/* Size of the address */
	__u16			perm;		/* IO permission */
	__u16			prio;		/* IO priority */
	struct saddr		addr;		/* Remote server address */
};

/*
 * Ack for userspace about requested command.
 */
struct pohmelfs_cn_ack {
	struct cn_msg		msg;
	int			error;
	int			msg_num;
	int			unused[3];
	struct pohmelfs_ctl	ctl;
};

/*
 * Inode info structure used to sync with server.
 * Check what stat() returns.
 */
struct netfs_inode_info {
	unsigned int		mode;
	unsigned int		nlink;
	unsigned int		uid;
	unsigned int		gid;
	unsigned int		blocksize;
	unsigned int		padding;
	__u64			ino;
	__u64			blocks;
	__u64			rdev;
	__u64			size;
	__u64			version;
};

static inline void netfs_convert_inode_info(struct netfs_inode_info *info)
{
	info->mode = __cpu_to_be32(info->mode);
	info->nlink = __cpu_to_be32(info->nlink);
	info->uid = __cpu_to_be32(info->uid);
	info->gid = __cpu_to_be32(info->gid);
	info->blocksize = __cpu_to_be32(info->blocksize);
	info->blocks = __cpu_to_be64(info->blocks);
	info->rdev = __cpu_to_be64(info->rdev);
	info->size = __cpu_to_be64(info->size);
	info->version = __cpu_to_be64(info->version);
	info->ino = __cpu_to_be64(info->ino);
}

/*
 * Cache state machine.
 */
enum {
	NETFS_COMMAND_PENDING = 0,	/* Command is being executed */
	NETFS_INODE_REMOTE_SYNCED,	/* Inode was synced to server */
	NETFS_INODE_REMOTE_DIR_SYNCED,	/* Inode (directory) was synced from the server */
	NETFS_INODE_OWNED,		/* Inode is owned by given host */
	NETFS_INODE_NEED_FLUSH,		/* Inode has to be flushed to the server */
};

/*
 * POHMELFS capabilities: information about supported
 * crypto operations (hash/cipher, modes, key sizes and so on),
 * root information (used/available size, number of objects, permissions)
 */
enum pohmelfs_capabilities {
	POHMELFS_CRYPTO_CAPABILITIES = 0,
	POHMELFS_ROOT_CAPABILITIES,
};

/* Read-only mount */
#define POHMELFS_FLAGS_RO		(1<<0)
/* Extended attributes support on/off */
#define POHMELFS_FLAGS_XATTR		(1<<1)

struct netfs_root_capabilities {
	__u64			nr_files;
	__u64			used, avail;
	__u64			flags;
};

static inline void netfs_convert_root_capabilities(struct netfs_root_capabilities *cap)
{
	cap->nr_files = __cpu_to_be64(cap->nr_files);
	cap->used = __cpu_to_be64(cap->used);
	cap->avail = __cpu_to_be64(cap->avail);
	cap->flags = __cpu_to_be64(cap->flags);
}

struct netfs_crypto_capabilities {
	unsigned short		hash_strlen;	/* Hash string length, like "hmac(sha1) including 0 byte "*/
	unsigned short		cipher_strlen;	/* Cipher string length with the same format */
	unsigned int		cipher_keysize;	/* Cipher key size */
};

static inline void netfs_convert_crypto_capabilities(struct netfs_crypto_capabilities *cap)
{
	cap->hash_strlen = __cpu_to_be16(cap->hash_strlen);
	cap->cipher_strlen = __cpu_to_be16(cap->cipher_strlen);
	cap->cipher_keysize = __cpu_to_be32(cap->cipher_keysize);
}

enum pohmelfs_lock_type {
	POHMELFS_LOCK_GRAB	= (1<<15),

	POHMELFS_READ_LOCK	= 0,
	POHMELFS_WRITE_LOCK,
};

struct netfs_lock {
	__u64			start;
	__u64			ino;
	__u32			size;
	__u32			type;
};

static inline void netfs_convert_lock(struct netfs_lock *lock)
{
	lock->start = __cpu_to_be64(lock->start);
	lock->ino = __cpu_to_be64(lock->ino);
	lock->size = __cpu_to_be32(lock->size);
	lock->type = __cpu_to_be32(lock->type);
}

#ifdef __KERNEL__

#include <linux/kernel.h>
#include <linux/completion.h>
#include <linux/rbtree.h>
#include <linux/net.h>
#include <linux/poll.h>

/*
 * Private POHMELFS cache of objects in directory.
 */
struct pohmelfs_name {
	struct rb_node		hash_node;

	struct list_head	sync_create_entry;

	u64			ino;

	u32			hash;
	u32			mode;
	u32			len;

	char			*data;
};

/*
 * POHMELFS inode. Main object.
 */
struct pohmelfs_inode {
	struct list_head	inode_entry;		/* Entry in superblock list.
							 * Objects which are not bound to dentry require to be dropped
							 * in ->put_super()
							 */
	struct rb_root		hash_root;		/* The same, but indexed by name hash and len */
	struct mutex		offset_lock;		/* Protect both above trees */

	struct list_head	sync_create_list;	/* List of created but not yet synced to the server children */

	unsigned int		drop_count;

	int			lock_type;		/* How this inode is locked: read or write */

	int			error;			/* Transaction error for given inode */

	long			state;			/* State machine above */

	u64			ino;			/* Inode number */
	u64			total_len;		/* Total length of all children names, used to create offsets */

	struct inode		vfs_inode;
};

struct netfs_trans;
typedef int (*netfs_trans_complete_t)(struct page **pages, unsigned int page_num,
		void *private, int err);

struct netfs_state;
struct pohmelfs_sb;

struct netfs_trans {
	/*
	 * Transaction header and attached contiguous data live here.
	 */
	struct iovec			iovec;

	/*
	 * Pages attached to transaction.
	 */
	struct page			**pages;

	/*
	 * List and protecting lock for transaction destination
	 * network states.
	 */
	spinlock_t			dst_lock;
	struct list_head		dst_list;

	/*
	 * Number of users for given transaction.
	 * For example each network state attached to transaction
	 * via dst_list increases it.
	 */
	atomic_t			refcnt;

	/*
	 * Number of pages attached to given transaction.
	 * Some slots in above page array can be NULL, since
	 * for example page can be under writeback already,
	 * so we skip it in this transaction.
	 */
	unsigned int			page_num;

	/*
	 * Transaction flags: single dst or broadcast and so on.
	 */
	unsigned int			flags;

	/*
	 * Size of the data, which can be placed into
	 * iovec.iov_base area.
	 */
	unsigned int			total_size;

	/*
	 * Number of pages to be sent to remote server.
	 * Usually equal to above page_num, but in case of partial
	 * writeback it can accumulate only pages already completed
	 * previous writeback.
	 */
	unsigned int			attached_pages;

	/*
	 * Attached number of bytes in all above pages.
	 */
	unsigned int			attached_size;

	/*
	 * Unique transacton generation number.
	 * Used as identity in the network state tree of transactions.
	 */
	unsigned int			gen;

	/*
	 * Transaction completion status.
	 */
	int				result;

	/*
	 * Superblock this transaction belongs to
	 */
	struct pohmelfs_sb		*psb;

	/*
	 * Crypto engine, which processed this transaction.
	 * Can be not NULL only if crypto engine holds encrypted pages.
	 */
	struct pohmelfs_crypto_engine	*eng;

	/* Private data */
	void				*private;

	/* Completion callback, invoked just before transaction is destroyed */
	netfs_trans_complete_t		complete;
};

static inline int netfs_trans_cur_len(struct netfs_trans *t)
{
	return (signed)(t->total_size - t->iovec.iov_len);
}

static inline void *netfs_trans_current(struct netfs_trans *t)
{
	return t->iovec.iov_base + t->iovec.iov_len;
}

struct netfs_trans *netfs_trans_alloc(struct pohmelfs_sb *psb, unsigned int size,
		unsigned int flags, unsigned int nr);
void netfs_trans_free(struct netfs_trans *t);
int netfs_trans_finish(struct netfs_trans *t, struct pohmelfs_sb *psb);
int netfs_trans_finish_send(struct netfs_trans *t, struct pohmelfs_sb *psb);

static inline void netfs_trans_reset(struct netfs_trans *t)
{
	t->complete = NULL;
}

struct netfs_trans_dst {
	struct list_head		trans_entry;
	struct rb_node			state_entry;

	unsigned long			send_time;

	/*
	 * Times this transaction was resent to its old or new,
	 * depending on flags, destinations. When it reaches maximum
	 * allowed number, specified in superblock->trans_retries,
	 * transaction will be freed with ETIMEDOUT error.
	 */
	unsigned int			retries;

	struct netfs_trans		*trans;
	struct netfs_state		*state;
};

struct netfs_trans_dst *netfs_trans_search(struct netfs_state *st, unsigned int gen);
void netfs_trans_drop_dst(struct netfs_trans_dst *dst);
void netfs_trans_drop_dst_nostate(struct netfs_trans_dst *dst);
void netfs_trans_drop_trans(struct netfs_trans *t, struct netfs_state *st);
void netfs_trans_drop_last(struct netfs_trans *t, struct netfs_state *st);
int netfs_trans_resend(struct netfs_trans *t, struct pohmelfs_sb *psb);
int netfs_trans_remove_nolock(struct netfs_trans_dst *dst, struct netfs_state *st);

int netfs_trans_init(void);
void netfs_trans_exit(void);

struct pohmelfs_crypto_engine {
	u64				iv;		/* Crypto IV for current operation */
	unsigned long			timeout;	/* Crypto waiting timeout */
	unsigned int			size;		/* Size of crypto scratchpad */
	void				*data;		/* Temporal crypto scratchpad */
	/*
	 * Crypto operations performed on objects.
	 */
	struct crypto_hash		*hash;
	struct crypto_ablkcipher	*cipher;

	struct pohmelfs_crypto_thread	*thread;	/* Crypto thread which hosts this engine */

	struct page			**pages;
	unsigned int			page_num;
};

struct pohmelfs_crypto_thread {
	struct list_head		thread_entry;

	struct task_struct		*thread;
	struct pohmelfs_sb		*psb;

	struct pohmelfs_crypto_engine	eng;

	struct netfs_trans		*trans;

	wait_queue_head_t		wait;
	int				error;

	unsigned int			size;
	struct page			*page;
};

void pohmelfs_crypto_thread_make_ready(struct pohmelfs_crypto_thread *th);

/*
 * Network state, attached to one server.
 */
struct netfs_state {
	struct mutex		__state_lock;		/* Can not allow to use the same socket simultaneously */
	struct mutex		__state_send_lock;
	struct netfs_cmd	cmd;			/* Cached command */
	struct netfs_inode_info	info;			/* Cached inode info */

	void			*data;			/* Cached some data */
	unsigned int		size;			/* Size of that data */

	struct pohmelfs_sb	*psb;			/* Superblock */

	struct task_struct	*thread;		/* Async receiving thread */

	/* Waiting/polling machinery */
	wait_queue_t		wait;
	wait_queue_head_t	*whead;
	wait_queue_head_t	thread_wait;

	struct mutex		trans_lock;
	struct rb_root		trans_root;

	struct pohmelfs_ctl	ctl;			/* Remote peer */

	struct socket		*socket;		/* Socket object */
	struct socket		*read_socket;		/* Cached pointer to socket object.
							 * Used to determine if between lock drops socket was changed.
							 * Never used to read data or any kind of access.
							 */
	/*
	 * Crypto engines to process incoming data.
	 */
	struct pohmelfs_crypto_engine	eng;

	int			need_reset;
};

int netfs_state_init(struct netfs_state *st);
void netfs_state_exit(struct netfs_state *st);

static inline void netfs_state_lock_send(struct netfs_state *st)
{
	mutex_lock(&st->__state_send_lock);
}

static inline int netfs_state_trylock_send(struct netfs_state *st)
{
	return mutex_trylock(&st->__state_send_lock);
}

static inline void netfs_state_unlock_send(struct netfs_state *st)
{
	BUG_ON(!mutex_is_locked(&st->__state_send_lock));

	mutex_unlock(&st->__state_send_lock);
}

static inline void netfs_state_lock(struct netfs_state *st)
{
	mutex_lock(&st->__state_lock);
}

static inline void netfs_state_unlock(struct netfs_state *st)
{
	BUG_ON(!mutex_is_locked(&st->__state_lock));

	mutex_unlock(&st->__state_lock);
}

static inline unsigned int netfs_state_poll(struct netfs_state *st)
{
	unsigned int revents = POLLHUP | POLLERR;

	netfs_state_lock(st);
	if (st->socket)
		revents = st->socket->ops->poll(NULL, st->socket, NULL);
	netfs_state_unlock(st);

	return revents;
}

struct pohmelfs_config;

struct pohmelfs_sb {
	struct rb_root		mcache_root;
	struct mutex		mcache_lock;
	atomic_long_t		mcache_gen;
	unsigned long		mcache_timeout;

	unsigned int		idx;

	unsigned int		trans_retries;

	atomic_t		trans_gen;

	unsigned int		crypto_attached_size;
	unsigned int		crypto_align_size;

	unsigned int		crypto_fail_unsupported;

	unsigned int		crypto_thread_num;
	struct list_head	crypto_active_list, crypto_ready_list;
	struct mutex		crypto_thread_lock;

	unsigned int		trans_max_pages;
	unsigned long		trans_data_size;
	unsigned long		trans_timeout;

	unsigned long		drop_scan_timeout;
	unsigned long		trans_scan_timeout;

	unsigned long		wait_on_page_timeout;

	struct list_head	flush_list;
	struct list_head	drop_list;
	spinlock_t		ino_lock;
	u64			ino;

	/*
	 * Remote nodes POHMELFS connected to.
	 */
	struct list_head	state_list;
	struct mutex		state_lock;

	/*
	 * Currently active state to request data from.
	 */
	struct pohmelfs_config	*active_state;


	wait_queue_head_t	wait;

	/*
	 * Timed checks: stale transactions, inodes to be freed and so on.
	 */
	struct delayed_work	dwork;
	struct delayed_work	drop_dwork;

	struct super_block	*sb;

	struct backing_dev_info	bdi;

	/*
	 * Algorithm strings.
	 */
	char			*hash_string;
	char			*cipher_string;

	u8			*hash_key;
	u8			*cipher_key;

	/*
	 * Algorithm string lengths.
	 */
	unsigned int		hash_strlen;
	unsigned int		cipher_strlen;
	unsigned int		hash_keysize;
	unsigned int		cipher_keysize;

	/*
	 * Controls whether to perfrom crypto processing or not.
	 */
	int			perform_crypto;

	/*
	 * POHMELFS statistics.
	 */
	u64			total_size;
	u64			avail_size;
	atomic_long_t		total_inodes;

	/*
	 * Xattr support, read-only and so on.
	 */
	u64			state_flags;

	/*
	 * Temporary storage to detect changes in the wait queue.
	 */
	long			flags;
};

static inline void netfs_trans_update(struct netfs_cmd *cmd,
		struct netfs_trans *t, unsigned int size)
{
	unsigned int sz = ALIGN(size, t->psb->crypto_align_size);

	t->iovec.iov_len += sizeof(struct netfs_cmd) + sz;
	cmd->cpad = __cpu_to_be16(sz - size);
}

static inline struct pohmelfs_sb *POHMELFS_SB(struct super_block *sb)
{
	return sb->s_fs_info;
}

static inline struct pohmelfs_inode *POHMELFS_I(struct inode *inode)
{
	return container_of(inode, struct pohmelfs_inode, vfs_inode);
}

static inline u64 pohmelfs_new_ino(struct pohmelfs_sb *psb)
{
	u64 ino;

	spin_lock(&psb->ino_lock);
	ino = psb->ino++;
	spin_unlock(&psb->ino_lock);

	return ino;
}

static inline void pohmelfs_put_inode(struct pohmelfs_inode *pi)
{
	struct pohmelfs_sb *psb = POHMELFS_SB(pi->vfs_inode.i_sb);

	spin_lock(&psb->ino_lock);
	list_move_tail(&pi->inode_entry, &psb->drop_list);
	pi->drop_count++;
	spin_unlock(&psb->ino_lock);
}

struct pohmelfs_config {
	struct list_head	config_entry;

	struct netfs_state	state;
};

struct pohmelfs_config_group {
	/*
	 * Entry in the global config group list.
	 */
	struct list_head	group_entry;

	/*
	 * Index of the current group.
	 */
	unsigned int		idx;
	/*
	 * Number of config_list entries in this group entry.
	 */
	unsigned int		num_entry;
	/*
	 * Algorithm strings.
	 */
	char			*hash_string;
	char			*cipher_string;

	/*
	 * Algorithm string lengths.
	 */
	unsigned int		hash_strlen;
	unsigned int		cipher_strlen;

	/*
	 * Key and its size.
	 */
	unsigned int		hash_keysize;
	unsigned int		cipher_keysize;
	u8			*hash_key;
	u8			*cipher_key;

	/*
	 * List of config entries (network state info) for given idx.
	 */
	struct list_head	config_list;
};

int __init pohmelfs_config_init(void);
void pohmelfs_config_exit(void);
int pohmelfs_copy_config(struct pohmelfs_sb *psb);
int pohmelfs_copy_crypto(struct pohmelfs_sb *psb);
int pohmelfs_config_check(struct pohmelfs_config *config, int idx);
int pohmelfs_state_init_one(struct pohmelfs_sb *psb, struct pohmelfs_config *conf);

extern const struct file_operations pohmelfs_dir_fops;
extern const struct inode_operations pohmelfs_dir_inode_ops;

int pohmelfs_state_init(struct pohmelfs_sb *psb);
void pohmelfs_state_exit(struct pohmelfs_sb *psb);
void pohmelfs_state_flush_transactions(struct netfs_state *st);

void pohmelfs_fill_inode(struct inode *inode, struct netfs_inode_info *info);

void pohmelfs_name_del(struct pohmelfs_inode *parent, struct pohmelfs_name *n);
void pohmelfs_free_names(struct pohmelfs_inode *parent);
struct pohmelfs_name *pohmelfs_search_hash(struct pohmelfs_inode *pi, u32 hash);

void pohmelfs_inode_del_inode(struct pohmelfs_sb *psb, struct pohmelfs_inode *pi);

struct pohmelfs_inode *pohmelfs_create_entry_local(struct pohmelfs_sb *psb,
	struct pohmelfs_inode *parent, struct qstr *str, u64 start, int mode);

int pohmelfs_write_create_inode(struct pohmelfs_inode *pi);

int pohmelfs_write_inode_create(struct inode *inode, struct netfs_trans *trans);
int pohmelfs_remove_child(struct pohmelfs_inode *parent, struct pohmelfs_name *n);

struct pohmelfs_inode *pohmelfs_new_inode(struct pohmelfs_sb *psb,
		struct pohmelfs_inode *parent, struct qstr *str,
		struct netfs_inode_info *info, int link);

int pohmelfs_setattr(struct dentry *dentry, struct iattr *attr);
int pohmelfs_setattr_raw(struct inode *inode, struct iattr *attr);

int pohmelfs_meta_command(struct pohmelfs_inode *pi, unsigned int cmd_op, unsigned int flags,
		netfs_trans_complete_t complete, void *priv, u64 start);
int pohmelfs_meta_command_data(struct pohmelfs_inode *pi, u64 id, unsigned int cmd_op, char *addon,
		unsigned int flags, netfs_trans_complete_t complete, void *priv, u64 start);

void pohmelfs_check_states(struct pohmelfs_sb *psb);
void pohmelfs_switch_active(struct pohmelfs_sb *psb);

int pohmelfs_construct_path_string(struct pohmelfs_inode *pi, void *data, int len);
int pohmelfs_path_length(struct pohmelfs_inode *pi);

struct pohmelfs_crypto_completion {
	struct completion	complete;
	int			error;
};

int pohmelfs_trans_crypt(struct netfs_trans *t, struct pohmelfs_sb *psb);
void pohmelfs_crypto_exit(struct pohmelfs_sb *psb);
int pohmelfs_crypto_init(struct pohmelfs_sb *psb);

int pohmelfs_crypto_engine_init(struct pohmelfs_crypto_engine *e, struct pohmelfs_sb *psb);
void pohmelfs_crypto_engine_exit(struct pohmelfs_crypto_engine *e);

int pohmelfs_crypto_process_input_data(struct pohmelfs_crypto_engine *e, u64 iv,
		void *data, struct page *page, unsigned int size);
int pohmelfs_crypto_process_input_page(struct pohmelfs_crypto_engine *e,
		struct page *page, unsigned int size, u64 iv);

static inline u64 pohmelfs_gen_iv(struct netfs_trans *t)
{
	u64 iv = t->gen;

	iv <<= 32;
	iv |= ((unsigned long)t) & 0xffffffff;

	return iv;
}

int pohmelfs_data_lock(struct pohmelfs_inode *pi, u64 start, u32 size, int type);
int pohmelfs_data_unlock(struct pohmelfs_inode *pi, u64 start, u32 size, int type);
int pohmelfs_data_lock_response(struct netfs_state *st);

static inline int pohmelfs_need_lock(struct pohmelfs_inode *pi, int type)
{
	if (test_bit(NETFS_INODE_OWNED, &pi->state)) {
		if (type == pi->lock_type)
			return 0;
		if ((type == POHMELFS_READ_LOCK) && (pi->lock_type == POHMELFS_WRITE_LOCK))
			return 0;
	}

	if (!test_bit(NETFS_INODE_REMOTE_SYNCED, &pi->state))
		return 0;

	return 1;
}

int __init pohmelfs_mcache_init(void);
void pohmelfs_mcache_exit(void);

/* #define CONFIG_POHMELFS_DEBUG */

#ifdef CONFIG_POHMELFS_DEBUG
#define dprintka(f, a...) printk(f, ##a)
#define dprintk(f, a...) printk("%d: " f, task_pid_vnr(current), ##a)
#else
#define dprintka(f, a...) do {} while (0)
#define dprintk(f, a...) do {} while (0)
#endif

static inline void netfs_trans_get(struct netfs_trans *t)
{
	atomic_inc(&t->refcnt);
}

static inline void netfs_trans_put(struct netfs_trans *t)
{
	if (atomic_dec_and_test(&t->refcnt)) {
		dprintk("%s: t: %p, gen: %u, err: %d.\n",
			__func__, t, t->gen, t->result);
		if (t->complete)
			t->complete(t->pages, t->page_num,
				t->private, t->result);
		netfs_trans_free(t);
	}
}

struct pohmelfs_mcache {
	struct rb_node			mcache_entry;
	struct completion		complete;

	atomic_t			refcnt;

	u64				gen;

	void				*data;
	u64				start;
	u32				size;
	int				err;

	struct netfs_inode_info		info;
};

struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start,
		unsigned int size, void *data);
void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m);
struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen);
void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m);

static inline void pohmelfs_mcache_get(struct pohmelfs_mcache *m)
{
	atomic_inc(&m->refcnt);
}

static inline void pohmelfs_mcache_put(struct pohmelfs_sb *psb,
		struct pohmelfs_mcache *m)
{
	if (atomic_dec_and_test(&m->refcnt))
		pohmelfs_mcache_free(psb, m);
}

/*#define POHMELFS_TRUNCATE_ON_INODE_FLUSH
 */

#endif /* __KERNEL__*/

#endif /* __NETFS_H */