Ejemplo n.º 1
0
static
void ctx_list_destroy_pf(struct hlist_head *head)
{
	struct ptlrpc_cli_ctx *ctx;

	while (!hlist_empty(head)) {
		ctx = hlist_entry(head->first, struct ptlrpc_cli_ctx,
				      cc_cache);

		LASSERT(atomic_read(&ctx->cc_refcount) == 0);
		LASSERT(test_bit(PTLRPC_CTX_CACHED_BIT,
				     &ctx->cc_flags) == 0);

		hlist_del_init(&ctx->cc_cache);
		ctx_destroy_pf(ctx->cc_sec, ctx);
	}
}
Ejemplo n.º 2
0
/**
 * Add for existing key into multimap.
 *
 * @tree_cell_head tree cell head.
 * @newcell cell to be added.
 *
 * @return 0 in success, -EEXIST when the same key-value pair already exists.
 */
static int multimap_add_oldkey(struct tree_cell_head *chead, struct tree_cell *newcell)
{
	struct tree_cell *cell;
	struct hlist_head *hlhead;
	struct hlist_node *hlnext;
	int found;

	ASSERT(chead);
	ASSERT(newcell);

	hlhead = &chead->head;
	ASSERT(!hlist_empty(hlhead));

	found = 0;
	hlist_for_each_entry_safe(cell, hlnext, hlhead, list) {
		ASSERT_TREECELL(cell);
		if (cell->val == newcell->val) { found++; break; }
	}
Ejemplo n.º 3
0
void fastcall detach_pid(struct task_struct *task, enum pid_type type)
{
	struct pid_link *link;
	struct pid *pid;
	int tmp;

	link = &task->pids[type];
	pid = link->pid;

	hlist_del_rcu(&link->node);
	link->pid = NULL;

	for (tmp = PIDTYPE_MAX; --tmp >= 0; )
		if (!hlist_empty(&pid->tasks[tmp]))
			return;

	free_pid(pid);
}
Ejemplo n.º 4
0
void show_buffers_(map_t *map, int all)
{
	struct buffer_head *buffer;
	unsigned i;

	for (i = 0; i < BUFFER_BUCKETS; i++) {
		struct hlist_head *bucket = &map->hash[i];
		if (hlist_empty(bucket))
			continue;

		printf("[%i] ", i);
		hlist_for_each_entry(buffer, bucket, hashlink) {
			if (all || buffer->count >= !hlist_unhashed(&buffer->hashlink) + 1)
				show_buffer(buffer);
		}
		printf("\n");
	}
}
Ejemplo n.º 5
0
// stap_destroy_vma_map(): Unconditionally destroys vma entries.
// Nothing should be using it anymore. Doesn't lock anything and just
// frees all items.
static void
stap_destroy_vma_map(void)
{
	if (__stp_tf_vma_map != NULL) {
		int i;
		for (i = 0; i < __STP_TF_TABLE_SIZE; i++) {
			struct hlist_head *head = &__stp_tf_vma_map[i];
			struct hlist_node *node;
			struct hlist_node *n;
			struct __stp_tf_vma_entry *entry = NULL;

			if (hlist_empty(head))
				continue;

		        stap_hlist_for_each_entry_safe(entry, node, n, head, hlist) {
				hlist_del(&entry->hlist);
				__stp_tf_vma_release_entry(entry);
			}
		}
Ejemplo n.º 6
0
void *slab_alloc(ohc_slab_t *slab)
{
	slab_block_t *sblock;
	uintptr_t leader;
	struct hlist_node *p;
	int buckets;
	int i;

	if(hlist_empty(&slab->block_head)) {
		buckets = slab_buckets(slab);
		sblock = malloc(sizeof(slab_block_t) + slab->item_size * buckets);
		if(sblock == NULL) {
			return NULL;
		}

		sblock->slab = slab;
		sblock->frees = buckets;
		hlist_add_head(&sblock->block_node, &slab->block_head);
		INIT_HLIST_HEAD(&sblock->item_head);

		leader = (uintptr_t)sblock + sizeof(slab_block_t);
		for(i = 0; i < buckets; i++) {
			*((slab_block_t **)leader) = sblock;
			p = (struct hlist_node *)(leader + sizeof(slab_block_t *));
			hlist_add_head(p, &sblock->item_head);
			leader += slab->item_size;
		}

	} else {
		sblock = list_entry(slab->block_head.first, slab_block_t, block_node);
	}

	p = sblock->item_head.first;
	hlist_del(p);

	sblock->frees--;
	if(sblock->frees == 0) {
		/* if no free items, we throw the block away */
		hlist_del(&sblock->block_node);
	}

	return p;
}
Ejemplo n.º 7
0
HYFI_MC_STATIC unsigned int mc_forward_hook(unsigned int hooknum, struct sk_buff *skb,
        const struct net_device *in, const struct net_device *out,
        int(*okfn)(struct sk_buff *))
{ 
	struct hyfi_net_bridge *hyfi_br = hyfi_bridge_get_by_dev(out);
    struct mc_struct *mc = MC_DEV(hyfi_br);
	struct hlist_head *rhead = NULL;
    struct net_bridge_port *port;
    struct mc_mdb_entry *mdb = MC_SKB_CB(skb)->mdb;

    /* Leave filter */
    if (mdb && MC_SKB_CB(skb)->type == MC_LEAVE && 
            (atomic_read(&mdb->users) > 0))
        goto drop;

    if (MC_SKB_CB(skb)->type != MC_LEAVE && 
            MC_SKB_CB(skb)->type != MC_REPORT)
        goto accept;

    if ((port = hyfi_br_port_get(in)) == NULL || !mc )
        goto accept;
    
    /* Report/Leave forward */
    if (mc->rp.type == MC_RTPORT_DEFAULT) {
        if (ntohs(skb->protocol) == ETH_P_IP)
            rhead = &mc->rp.igmp_rlist;
#ifdef HYBRID_MC_MLD
        else
            rhead = &mc->rp.mld_rlist;
#endif
        if (!hlist_empty(rhead)) {
            struct mc_querier_entry *qe;
            struct hlist_node *h;

            hlist_for_each_entry_rcu(qe, h, rhead, rlist) {
                if (((struct net_bridge_port *)qe->port)->dev == out)
                    goto accept;
            }
            goto drop;
        }
    } else if (mc->rp.type == MC_RTPORT_SPECIFY) {
Ejemplo n.º 8
0
static void perf_syscall_enter(void *ignore, struct pt_regs *regs, long id)
{
	struct syscall_metadata *sys_data;
	struct syscall_trace_enter *rec;
	struct hlist_head *head;
	int syscall_nr;
	int rctx;
	int size;

	syscall_nr = trace_get_syscall_nr(current, regs);
	if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
		return;
	if (!test_bit(syscall_nr, enabled_perf_enter_syscalls))
		return;

	sys_data = syscall_nr_to_meta(syscall_nr);
	if (!sys_data)
		return;

	head = this_cpu_ptr(sys_data->enter_event->perf_events);
	if (hlist_empty(head))
		return;

	/* get the size after alignment with the u32 buffer size field */
	size = sizeof(unsigned long) * sys_data->nb_args + sizeof(*rec);
	size = ALIGN(size + sizeof(u32), sizeof(u64));
	size -= sizeof(u32);

	rec = perf_trace_buf_alloc(size, NULL, &rctx);
	if (!rec)
		return;

	rec->nr = syscall_nr;
	syscall_get_arguments(current, regs, 0, sys_data->nb_args,
			       (unsigned long *)&rec->args);
	perf_trace_buf_submit(rec, size, rctx,
			      sys_data->enter_event->event.type, 1, regs,
			      head, NULL);
}
Ejemplo n.º 9
0
/*
 *     Module 'remove' entry point.
 *     o delete /proc/net/router directory and static entries.
 */
static void __exit vlan_cleanup_module(void)
{
	int i;

	vlan_ioctl_set(NULL);

	/* Un-register us from receiving netdevice events */
	unregister_netdevice_notifier(&vlan_notifier_block);

	dev_remove_pack(&vlan_packet_type);
	vlan_cleanup_devices();

	/* This table must be empty if there are no module
	 * references left.
	 */
	for (i = 0; i < VLAN_GRP_HASH_SIZE; i++) {
		BUG_ON(!hlist_empty(&vlan_group_hash[i]));
	}
	vlan_proc_cleanup();

	synchronize_net();
}
Ejemplo n.º 10
0
void veip_cleanup(void)
{
	int i;
	struct veip_struct *veip;

	spin_lock(&veip_lock);
	for (i = 0; i < VEIP_HASH_SZ; i++)
		while (!hlist_empty(ip_entry_hash_table + i)) {
			struct ip_entry_struct *entry;

			entry = hlist_entry(ip_entry_hash_table[i].first,
					struct ip_entry_struct, ip_hash);
			hlist_del(&entry->ip_hash);
			list_del(&entry->ve_list);
			kfree(entry);
		}

	/*vzredir may remain some veip-s*/
	while (!list_empty(&veip_lh)) {
		veip = list_first_entry(&veip_lh, struct veip_struct, list);
		veip_put(veip);
	}
	spin_unlock(&veip_lock);
}
Ejemplo n.º 11
0
static void perf_syscall_exit(void *ignore, struct pt_regs *regs, long ret)
{
	struct syscall_metadata *sys_data;
	struct syscall_trace_exit *rec;
	struct hlist_head *head;
	int syscall_nr;
	int rctx;
	int size;

	syscall_nr = trace_get_syscall_nr(current, regs);
	if (syscall_nr < 0 || syscall_nr >= NR_syscalls)
		return;
	if (!test_bit(syscall_nr, enabled_perf_exit_syscalls))
		return;

	sys_data = syscall_nr_to_meta(syscall_nr);
	if (!sys_data)
		return;

	head = this_cpu_ptr(sys_data->exit_event->perf_events);
	if (hlist_empty(head))
		return;

	/* We can probably do that at build time */
	size = ALIGN(sizeof(*rec) + sizeof(u32), sizeof(u64));
	size -= sizeof(u32);

	rec = (struct syscall_trace_exit *)perf_trace_buf_prepare(size,
				sys_data->exit_event->event.type, NULL, &rctx);
	if (!rec)
		return;

	rec->nr = syscall_nr;
	rec->ret = syscall_get_return_value(current, regs);
	perf_trace_buf_submit(rec, size, rctx, 0, 1, regs, head, NULL);
}
Ejemplo n.º 12
0
static void pep_sock_unhash(struct sock *sk)
{
	struct pep_sock *pn = pep_sk(sk);
	struct sock *skparent = NULL;

	lock_sock(sk);
	if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) {
		skparent = pn->listener;
		release_sock(sk);

		pn = pep_sk(skparent);
		lock_sock(skparent);
		sk_del_node_init(sk);
		sk = skparent;
	}
	/* Unhash a listening sock only when it is closed
	 * and all of its active connected pipes are closed. */
	if (hlist_empty(&pn->hlist))
		pn_sock_unhash(&pn->pn_sk.sk);
	release_sock(sk);

	if (skparent)
		sock_put(skparent);
}
Ejemplo n.º 13
0
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
{
    struct rpc_message msg = {
        .rpc_argp = &data->args,
        .rpc_resp = &data->res,
        .rpc_cred = data->cred,
    };
    struct rpc_task_setup task_setup_data = {
        .rpc_message = &msg,
        .callback_ops = &nfs_unlink_ops,
        .callback_data = data,
        .workqueue = nfsiod_workqueue,
        .flags = RPC_TASK_ASYNC,
    };
    struct rpc_task *task;
    struct dentry *alias;

    alias = d_lookup(parent, &data->args.name);
    if (alias != NULL) {
        int ret;
        void *devname_garbage = NULL;

        /*
         * Hey, we raced with lookup... See if we need to transfer
         * the sillyrename information to the aliased dentry.
         */
        nfs_free_dname(data);
        ret = nfs_copy_dname(alias, data);
        spin_lock(&alias->d_lock);
        if (ret == 0 && alias->d_inode != NULL &&
                !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
            devname_garbage = alias->d_fsdata;
            alias->d_fsdata = data;
            alias->d_flags |= DCACHE_NFSFS_RENAMED;
            ret = 1;
        } else
            ret = 0;
        spin_unlock(&alias->d_lock);
        nfs_dec_sillycount(dir);
        dput(alias);
        /*
         * If we'd displaced old cached devname, free it.  At that
         * point dentry is definitely not a root, so we won't need
         * that anymore.
         */
        kfree(devname_garbage);
        return ret;
    }
    data->dir = igrab(dir);
    if (!data->dir) {
        nfs_dec_sillycount(dir);
        return 0;
    }
    nfs_sb_active(dir->i_sb);
    data->args.fh = NFS_FH(dir);
    nfs_fattr_init(data->res.dir_attr);

    NFS_PROTO(dir)->unlink_setup(&msg, dir);

    task_setup_data.rpc_client = NFS_CLIENT(dir);
    task = rpc_run_task(&task_setup_data);
    if (!IS_ERR(task))
        rpc_put_task_async(task);
    return 1;
}

static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
{
    struct dentry *parent;
    struct inode *dir;
    int ret = 0;


    parent = dget_parent(dentry);
    if (parent == NULL)
        goto out_free;
    dir = parent->d_inode;
    /* Non-exclusive lock protects against concurrent lookup() calls */
    spin_lock(&dir->i_lock);
    if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
        /* Deferred delete */
        hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
        spin_unlock(&dir->i_lock);
        ret = 1;
        goto out_dput;
    }
    spin_unlock(&dir->i_lock);
    ret = nfs_do_call_unlink(parent, dir, data);
out_dput:
    dput(parent);
out_free:
    return ret;
}

void nfs_block_sillyrename(struct dentry *dentry)
{
    struct nfs_inode *nfsi = NFS_I(dentry->d_inode);

    wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1);
}

void nfs_unblock_sillyrename(struct dentry *dentry)
{
    struct inode *dir = dentry->d_inode;
    struct nfs_inode *nfsi = NFS_I(dir);
    struct nfs_unlinkdata *data;

    atomic_inc(&nfsi->silly_count);
    spin_lock(&dir->i_lock);
    while (!hlist_empty(&nfsi->silly_list)) {
        if (!atomic_inc_not_zero(&nfsi->silly_count))
            break;
        data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list);
        hlist_del(&data->list);
        spin_unlock(&dir->i_lock);
        if (nfs_do_call_unlink(dentry, dir, data) == 0)
            nfs_free_unlinkdata(data);
        spin_lock(&dir->i_lock);
    }
    spin_unlock(&dir->i_lock);
}

/**
 * nfs_async_unlink - asynchronous unlinking of a file
 * @dir: parent directory of dentry
 * @dentry: dentry to unlink
 */
static int
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
{
    struct nfs_unlinkdata *data;
    int status = -ENOMEM;
    void *devname_garbage = NULL;

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    if (data == NULL)
        goto out;

    data->cred = rpc_lookup_cred();
    if (IS_ERR(data->cred)) {
        status = PTR_ERR(data->cred);
        goto out_free;
    }
    data->res.dir_attr = &data->dir_attr;

    status = -EBUSY;
    spin_lock(&dentry->d_lock);
    if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
        goto out_unlock;
    dentry->d_flags |= DCACHE_NFSFS_RENAMED;
    devname_garbage = dentry->d_fsdata;
    dentry->d_fsdata = data;
    spin_unlock(&dentry->d_lock);
    /*
     * If we'd displaced old cached devname, free it.  At that
     * point dentry is definitely not a root, so we won't need
     * that anymore.
     */
    if (devname_garbage)
        kfree(devname_garbage);
    return 0;
out_unlock:
    spin_unlock(&dentry->d_lock);
    put_rpccred(data->cred);
out_free:
    kfree(data);
out:
    return status;
}

/**
 * nfs_complete_unlink - Initialize completion of the sillydelete
 * @dentry: dentry to delete
 * @inode: inode
 *
 * Since we're most likely to be called by dentry_iput(), we
 * only use the dentry to find the sillydelete. We then copy the name
 * into the qstr.
 */
void
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
{
    struct nfs_unlinkdata	*data = NULL;

    spin_lock(&dentry->d_lock);
    if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
        dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
        data = dentry->d_fsdata;
        dentry->d_fsdata = NULL;
    }
    spin_unlock(&dentry->d_lock);

    if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
        nfs_free_unlinkdata(data);
}

/* Cancel a queued async unlink. Called when a sillyrename run fails. */
static void
nfs_cancel_async_unlink(struct dentry *dentry)
{
    spin_lock(&dentry->d_lock);
    if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
        struct nfs_unlinkdata *data = dentry->d_fsdata;

        dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
        dentry->d_fsdata = NULL;
        spin_unlock(&dentry->d_lock);
        nfs_free_unlinkdata(data);
        return;
    }
    spin_unlock(&dentry->d_lock);
}
Ejemplo n.º 14
0
Archivo: udp.c Proyecto: gizm0n/wl500g
/**
 *  __udp_lib_get_port  -  UDP/-Lite port lookup for IPv4 and IPv6
 *
 *  @sk:          socket struct in question
 *  @snum:        port number to look up
 *  @udptable:    hash list table, must be of UDP_HTABLE_SIZE
 *  @saddr_comp:  AF-dependent comparison of bound local IP addresses
 */
int __udp_lib_get_port(struct sock *sk, unsigned short snum,
		       struct hlist_head udptable[],
		       int (*saddr_comp)(const struct sock *sk1,
					 const struct sock *sk2 )    )
{
	struct hlist_node *node;
	struct hlist_head *head;
	struct sock *sk2;
	int    error = 1;

	write_lock_bh(&udp_hash_lock);

	if (!snum) {
		int i;
		int low = sysctl_local_port_range[0];
		int high = sysctl_local_port_range[1];
		unsigned rover, best, best_size_so_far;

		best_size_so_far = UINT_MAX;
		best = rover = net_random() % (high - low) + low;

		/* 1st pass: look for empty (or shortest) hash chain */
		for (i = 0; i < UDP_HTABLE_SIZE; i++) {
			int size = 0;

			head = &udptable[rover & (UDP_HTABLE_SIZE - 1)];
			if (hlist_empty(head))
				goto gotit;

			sk_for_each(sk2, node, head) {
				if (++size >= best_size_so_far)
					goto next;
			}
			best_size_so_far = size;
			best = rover;
		next:
			/* fold back if end of range */
			if (++rover > high)
				rover = low + ((rover - low)
					       & (UDP_HTABLE_SIZE - 1));


		}

		/* 2nd pass: find hole in shortest hash chain */
		rover = best;
		for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
			if (! __udp_lib_lport_inuse(rover, udptable))
				goto gotit;
			rover += UDP_HTABLE_SIZE;
			if (rover > high)
				rover = low + ((rover - low)
					       & (UDP_HTABLE_SIZE - 1));
		}


		/* All ports in use! */
		goto fail;

gotit:
		snum = rover;
	} else {
Ejemplo n.º 15
0
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
{
	struct rpc_message msg = {
		.rpc_argp = &data->args,
		.rpc_resp = &data->res,
		.rpc_cred = data->cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_message = &msg,
		.callback_ops = &nfs_unlink_ops,
		.callback_data = data,
		.workqueue = nfsiod_workqueue,
		.flags = RPC_TASK_ASYNC,
	};
	struct rpc_task *task;
	struct dentry *alias;

	alias = d_lookup(parent, &data->args.name);
	if (alias != NULL) {
		int ret;
		void *devname_garbage = NULL;

		nfs_free_dname(data);
		ret = nfs_copy_dname(alias, data);
		spin_lock(&alias->d_lock);
		if (ret == 0 && alias->d_inode != NULL &&
		    !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
			devname_garbage = alias->d_fsdata;
			alias->d_fsdata = data;
			alias->d_flags |= DCACHE_NFSFS_RENAMED;
			ret = 1;
		} else
			ret = 0;
		spin_unlock(&alias->d_lock);
		nfs_dec_sillycount(dir);
		dput(alias);
		kfree(devname_garbage);
		return ret;
	}
	data->dir = igrab(dir);
	if (!data->dir) {
		nfs_dec_sillycount(dir);
		return 0;
	}
	nfs_sb_active(dir->i_sb);
	data->args.fh = NFS_FH(dir);
	nfs_fattr_init(data->res.dir_attr);

	NFS_PROTO(dir)->unlink_setup(&msg, dir);

	task_setup_data.rpc_client = NFS_CLIENT(dir);
	task = rpc_run_task(&task_setup_data);
	if (!IS_ERR(task))
		rpc_put_task_async(task);
	return 1;
}

static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
{
	struct dentry *parent;
	struct inode *dir;
	int ret = 0;


	parent = dget_parent(dentry);
	if (parent == NULL)
		goto out_free;
	dir = parent->d_inode;
	
	spin_lock(&dir->i_lock);
	if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
		
		hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
		spin_unlock(&dir->i_lock);
		ret = 1;
		goto out_dput;
	}
	spin_unlock(&dir->i_lock);
	ret = nfs_do_call_unlink(parent, dir, data);
out_dput:
	dput(parent);
out_free:
	return ret;
}

void nfs_block_sillyrename(struct dentry *dentry)
{
	struct nfs_inode *nfsi = NFS_I(dentry->d_inode);

	wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1);
}

void nfs_unblock_sillyrename(struct dentry *dentry)
{
	struct inode *dir = dentry->d_inode;
	struct nfs_inode *nfsi = NFS_I(dir);
	struct nfs_unlinkdata *data;

	atomic_inc(&nfsi->silly_count);
	spin_lock(&dir->i_lock);
	while (!hlist_empty(&nfsi->silly_list)) {
		if (!atomic_inc_not_zero(&nfsi->silly_count))
			break;
		data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list);
		hlist_del(&data->list);
		spin_unlock(&dir->i_lock);
		if (nfs_do_call_unlink(dentry, dir, data) == 0)
			nfs_free_unlinkdata(data);
		spin_lock(&dir->i_lock);
	}
	spin_unlock(&dir->i_lock);
}

static int
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
{
	struct nfs_unlinkdata *data;
	int status = -ENOMEM;
	void *devname_garbage = NULL;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data == NULL)
		goto out;

	data->cred = rpc_lookup_cred();
	if (IS_ERR(data->cred)) {
		status = PTR_ERR(data->cred);
		goto out_free;
	}
	data->res.dir_attr = &data->dir_attr;

	status = -EBUSY;
	spin_lock(&dentry->d_lock);
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out_unlock;
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
	devname_garbage = dentry->d_fsdata;
	dentry->d_fsdata = data;
	spin_unlock(&dentry->d_lock);
	if (devname_garbage)
		kfree(devname_garbage);
	return 0;
out_unlock:
	spin_unlock(&dentry->d_lock);
	put_rpccred(data->cred);
out_free:
	kfree(data);
out:
	return status;
}

void
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
{
	struct nfs_unlinkdata	*data = NULL;

	spin_lock(&dentry->d_lock);
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		data = dentry->d_fsdata;
		dentry->d_fsdata = NULL;
	}
	spin_unlock(&dentry->d_lock);

	if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
		nfs_free_unlinkdata(data);
}

static void
nfs_cancel_async_unlink(struct dentry *dentry)
{
	spin_lock(&dentry->d_lock);
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		struct nfs_unlinkdata *data = dentry->d_fsdata;

		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		dentry->d_fsdata = NULL;
		spin_unlock(&dentry->d_lock);
		nfs_free_unlinkdata(data);
		return;
	}
	spin_unlock(&dentry->d_lock);
}
Ejemplo n.º 16
0
static int nfs_do_call_unlink(struct dentry *parent, struct inode *dir, struct nfs_unlinkdata *data)
{
	struct rpc_message msg = {
		.rpc_argp = &data->args,
		.rpc_resp = &data->res,
		.rpc_cred = data->cred,
	};
	struct rpc_task_setup task_setup_data = {
		.rpc_message = &msg,
		.callback_ops = &nfs_unlink_ops,
		.callback_data = data,
		.workqueue = nfsiod_workqueue,
		.flags = RPC_TASK_ASYNC,
	};
	struct rpc_task *task;
	struct dentry *alias;

	alias = d_lookup(parent, &data->args.name);
	if (alias != NULL) {
		int ret = 0;

		/*
		 * Hey, we raced with lookup... See if we need to transfer
		 * the sillyrename information to the aliased dentry.
		 */
		nfs_free_dname(data);
		spin_lock(&alias->d_lock);
		if (alias->d_inode != NULL &&
		    !(alias->d_flags & DCACHE_NFSFS_RENAMED)) {
			alias->d_fsdata = data;
			alias->d_flags |= DCACHE_NFSFS_RENAMED;
			ret = 1;
		}
		spin_unlock(&alias->d_lock);
		nfs_dec_sillycount(dir);
		dput(alias);
		return ret;
	}
	data->dir = igrab(dir);
	if (!data->dir) {
		nfs_dec_sillycount(dir);
		return 0;
	}
	nfs_sb_active(dir->i_sb);
	data->args.fh = NFS_FH(dir);
	nfs_fattr_init(data->res.dir_attr);

	NFS_PROTO(dir)->unlink_setup(&msg, dir);

	task_setup_data.rpc_client = NFS_CLIENT(dir);
	task = rpc_run_task(&task_setup_data);
	if (!IS_ERR(task))
		rpc_put_task(task);
	return 1;
}

static int nfs_call_unlink(struct dentry *dentry, struct nfs_unlinkdata *data)
{
	struct dentry *parent;
	struct inode *dir;
	int ret = 0;


	parent = dget_parent(dentry);
	if (parent == NULL)
		goto out_free;
	dir = parent->d_inode;
	if (nfs_copy_dname(dentry, data) != 0)
		goto out_dput;
	/* Non-exclusive lock protects against concurrent lookup() calls */
	spin_lock(&dir->i_lock);
	if (atomic_inc_not_zero(&NFS_I(dir)->silly_count) == 0) {
		/* Deferred delete */
		hlist_add_head(&data->list, &NFS_I(dir)->silly_list);
		spin_unlock(&dir->i_lock);
		ret = 1;
		goto out_dput;
	}
	spin_unlock(&dir->i_lock);
	ret = nfs_do_call_unlink(parent, dir, data);
out_dput:
	dput(parent);
out_free:
	return ret;
}

void nfs_block_sillyrename(struct dentry *dentry)
{
	struct nfs_inode *nfsi = NFS_I(dentry->d_inode);

	wait_event(nfsi->waitqueue, atomic_cmpxchg(&nfsi->silly_count, 1, 0) == 1);
}

void nfs_unblock_sillyrename(struct dentry *dentry)
{
	struct inode *dir = dentry->d_inode;
	struct nfs_inode *nfsi = NFS_I(dir);
	struct nfs_unlinkdata *data;

	atomic_inc(&nfsi->silly_count);
	spin_lock(&dir->i_lock);
	while (!hlist_empty(&nfsi->silly_list)) {
		if (!atomic_inc_not_zero(&nfsi->silly_count))
			break;
		data = hlist_entry(nfsi->silly_list.first, struct nfs_unlinkdata, list);
		hlist_del(&data->list);
		spin_unlock(&dir->i_lock);
		if (nfs_do_call_unlink(dentry, dir, data) == 0)
			nfs_free_unlinkdata(data);
		spin_lock(&dir->i_lock);
	}
	spin_unlock(&dir->i_lock);
}

int
nfs_async_unlink(struct inode *dir, struct dentry *dentry)
{
	struct nfs_unlinkdata *data;
	int status = -ENOMEM;

	data = kzalloc(sizeof(*data), GFP_KERNEL);
	if (data == NULL)
		goto out;

	data->cred = rpc_lookup_cred();
	if (IS_ERR(data->cred)) {
		status = PTR_ERR(data->cred);
		goto out_free;
	}
	data->res.seq_res.sr_slotid = NFS4_MAX_SLOT_TABLE;
	data->res.dir_attr = &data->dir_attr;

	status = -EBUSY;
	spin_lock(&dentry->d_lock);
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED)
		goto out_unlock;
	dentry->d_flags |= DCACHE_NFSFS_RENAMED;
	dentry->d_fsdata = data;
	spin_unlock(&dentry->d_lock);
	return 0;
out_unlock:
	spin_unlock(&dentry->d_lock);
	put_rpccred(data->cred);
out_free:
	kfree(data);
out:
	return status;
}

void
nfs_complete_unlink(struct dentry *dentry, struct inode *inode)
{
	struct nfs_unlinkdata	*data = NULL;

	spin_lock(&dentry->d_lock);
	if (dentry->d_flags & DCACHE_NFSFS_RENAMED) {
		dentry->d_flags &= ~DCACHE_NFSFS_RENAMED;
		data = dentry->d_fsdata;
	}
	spin_unlock(&dentry->d_lock);

	if (data != NULL && (NFS_STALE(inode) || !nfs_call_unlink(dentry, data)))
		nfs_free_unlinkdata(data);
}
Ejemplo n.º 17
0
int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
	struct inode *inode = file->f_mapping->host;
	struct ext4_inode_info *ei = EXT4_I(inode);
	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
	int ret, err;
	tid_t commit_tid;
	bool needs_barrier = false;

	J_ASSERT(ext4_journal_current_handle() == NULL);

	trace_ext4_sync_file_enter(file, datasync);

	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret)
		return ret;
	mutex_lock(&inode->i_mutex);

	if (inode->i_sb->s_flags & MS_RDONLY)
		goto out;

	ret = ext4_flush_unwritten_io(inode);
	if (ret < 0)
		goto out;

	if (!journal) {
		ret = __sync_inode(inode, datasync);
		if (!ret && !hlist_empty(&inode->i_dentry))
			ret = ext4_sync_parent(inode);
		goto out;
	}

	/*
	 * data=writeback,ordered:
	 *  The caller's filemap_fdatawrite()/wait will sync the data.
	 *  Metadata is in the journal, we wait for proper transaction to
	 *  commit here.
	 *
	 * data=journal:
	 *  filemap_fdatawrite won't do anything (the buffers are clean).
	 *  ext4_force_commit will write the file data into the journal and
	 *  will wait on that.
	 *  filemap_fdatawait() will encounter a ton of newly-dirtied pages
	 *  (they were dirtied by commit).  But that's OK - the blocks are
	 *  safe in-journal, which is all fsync() needs to ensure.
	 */
	if (ext4_should_journal_data(inode)) {
#ifdef CONFIG_ANDROTRACE 
		/* fsync/fdatsync to directory 
  		  -> flushing all journal head in running transaction*/
		journal->j_fs_mapping = inode->i_mapping;
#endif
		ret = ext4_force_commit(inode->i_sb);
		goto out;
	}

	commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
	if (journal->j_flags & JBD2_BARRIER &&
	    !jbd2_trans_will_send_data_barrier(journal, commit_tid))
		needs_barrier = true;

#ifdef CONFIG_ANDROTRACE
	ret = jbd2_complete_transaction(journal, commit_tid, inode->i_mapping /*androtrace */);
#else
	ret = jbd2_complete_transaction(journal, commit_tid);
#endif
	if (needs_barrier) {
		err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
		if (!ret)
			ret = err;
	}
 out:
	mutex_unlock(&inode->i_mutex);
	trace_ext4_sync_file_exit(inode, ret);

#ifdef CONFIG_ANDROTRACE
	inode->i_mapping->t_tid = 0; /* journal_transaction id */
#endif
	return ret;
}
/* If printing "Bad invarians" then the test cannot be considered
 * valid, as something caused the loop to use more time.  Thus,
 * comparing it against another run could vary too much when counting
 * cycles.
 */
static struct my_obj* objhash_extract(struct my_obj *last_obj, bool no_match)
{
	/* Idea: get an object that does NOT match the prev page */
	struct my_obj *obj;
	u32 hash_idx;
	int skip_bucket = 0;

	if (last_obj == NULL) {
		hash_idx = 0;
	} else {
		hash_idx = jhash(&last_obj->page, 8, 13) % HASHSZ;
	}

	if (objhash_cnt < 2) {
		pr_warn("Bad invarians: request too many objects\n");
		return NULL;
	}

	/* With no_match, start looking in/from the next hash bucket */
	if (no_match) {
		hash_idx = (hash_idx + 1) % HASHSZ;
	}

	while(1) {
		struct hlist_head *hhead;
		struct hlist_node *tmp;

		hhead = &objhash[hash_idx];

		if (hlist_empty(hhead)) {
			skip_bucket++;
			hash_idx = (hash_idx + 1) % HASHSZ;
			//pr_info("Skip to next hash bucket[%d]\n", hash_idx);
			continue; /* Skip to next hash bucket */
		}

		hlist_for_each_entry_safe(obj, tmp, hhead, node) {
			if (no_match && last_obj && obj->page == last_obj->page)
				pr_warn("Bad invarians: return same page\n");

			/* When requesting a match, then there might
			 * not be any matching pages left in objhash.
			 * Thus don't try to match, just return obj.
			 */
			hlist_del(&obj->node);
			objhash_cnt--;

			/* Catch too much time on bucket search objects.
			 * Likely need better/more prefill
			 */
			if (skip_bucket >= (HASHSZ/2)) {
				pr_info("Bad invarians: "
					"search skipped many buckets: %d\n",
					skip_bucket);
				skip_bucket = 0;
			}
			return obj;
		}
	}
	pr_warn("Bad invarians: no object found/extracted\n");
	return NULL;
}
Ejemplo n.º 19
0
static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
			   struct netlink_callback *cb,
			   const struct smc_diag_req *req,
			   struct nlattr *bc)
{
	struct smc_sock *smc = smc_sk(sk);
	struct user_namespace *user_ns;
	struct smc_diag_msg *r;
	struct nlmsghdr *nlh;

	nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
			cb->nlh->nlmsg_type, sizeof(*r), NLM_F_MULTI);
	if (!nlh)
		return -EMSGSIZE;

	r = nlmsg_data(nlh);
	smc_diag_msg_common_fill(r, sk);
	r->diag_state = sk->sk_state;
	r->diag_fallback = smc->use_fallback;
	user_ns = sk_user_ns(NETLINK_CB(cb->skb).sk);
	if (smc_diag_msg_attrs_fill(sk, skb, r, user_ns))
		goto errout;

	if ((req->diag_ext & (1 << (SMC_DIAG_CONNINFO - 1))) &&
	    smc->conn.alert_token_local) {
		struct smc_connection *conn = &smc->conn;
		struct smc_diag_conninfo cinfo = {
			.token = conn->alert_token_local,
			.sndbuf_size = conn->sndbuf_desc ?
				conn->sndbuf_desc->len : 0,
			.rmbe_size = conn->rmb_desc ? conn->rmb_desc->len : 0,
			.peer_rmbe_size = conn->peer_rmbe_size,

			.rx_prod.wrap = conn->local_rx_ctrl.prod.wrap,
			.rx_prod.count = conn->local_rx_ctrl.prod.count,
			.rx_cons.wrap = conn->local_rx_ctrl.cons.wrap,
			.rx_cons.count = conn->local_rx_ctrl.cons.count,

			.tx_prod.wrap = conn->local_tx_ctrl.prod.wrap,
			.tx_prod.count = conn->local_tx_ctrl.prod.count,
			.tx_cons.wrap = conn->local_tx_ctrl.cons.wrap,
			.tx_cons.count = conn->local_tx_ctrl.cons.count,

			.tx_prod_flags =
				*(u8 *)&conn->local_tx_ctrl.prod_flags,
			.tx_conn_state_flags =
				*(u8 *)&conn->local_tx_ctrl.conn_state_flags,
			.rx_prod_flags = *(u8 *)&conn->local_rx_ctrl.prod_flags,
			.rx_conn_state_flags =
				*(u8 *)&conn->local_rx_ctrl.conn_state_flags,

			.tx_prep.wrap = conn->tx_curs_prep.wrap,
			.tx_prep.count = conn->tx_curs_prep.count,
			.tx_sent.wrap = conn->tx_curs_sent.wrap,
			.tx_sent.count = conn->tx_curs_sent.count,
			.tx_fin.wrap = conn->tx_curs_fin.wrap,
			.tx_fin.count = conn->tx_curs_fin.count,
		};

		if (nla_put(skb, SMC_DIAG_CONNINFO, sizeof(cinfo), &cinfo) < 0)
			goto errout;
	}

	if ((req->diag_ext & (1 << (SMC_DIAG_LGRINFO - 1))) && smc->conn.lgr &&
	    !list_empty(&smc->conn.lgr->list)) {
		struct smc_diag_lgrinfo linfo = {
			.role = smc->conn.lgr->role,
			.lnk[0].ibport = smc->conn.lgr->lnk[0].ibport,
			.lnk[0].link_id = smc->conn.lgr->lnk[0].link_id,
		};

		memcpy(linfo.lnk[0].ibname,
		       smc->conn.lgr->lnk[0].smcibdev->ibdev->name,
		       sizeof(smc->conn.lgr->lnk[0].smcibdev->ibdev->name));
		smc_gid_be16_convert(linfo.lnk[0].gid,
				     smc->conn.lgr->lnk[0].gid.raw);
		smc_gid_be16_convert(linfo.lnk[0].peer_gid,
				     smc->conn.lgr->lnk[0].peer_gid);

		if (nla_put(skb, SMC_DIAG_LGRINFO, sizeof(linfo), &linfo) < 0)
			goto errout;
	}

	nlmsg_end(skb, nlh);
	return 0;

errout:
	nlmsg_cancel(skb, nlh);
	return -EMSGSIZE;
}

static int smc_diag_dump_proto(struct proto *prot, struct sk_buff *skb,
			       struct netlink_callback *cb)
{
	struct net *net = sock_net(skb->sk);
	struct nlattr *bc = NULL;
	struct hlist_head *head;
	struct sock *sk;
	int rc = 0;

	read_lock(&prot->h.smc_hash->lock);
	head = &prot->h.smc_hash->ht;
	if (hlist_empty(head))
		goto out;

	sk_for_each(sk, head) {
		if (!net_eq(sock_net(sk), net))
			continue;
		rc = __smc_diag_dump(sk, skb, cb, nlmsg_data(cb->nlh), bc);
		if (rc)
			break;
	}

out:
	read_unlock(&prot->h.smc_hash->lock);
	return rc;
}

static int smc_diag_dump(struct sk_buff *skb, struct netlink_callback *cb)
{
	int rc = 0;

	rc = smc_diag_dump_proto(&smc_proto, skb, cb);
	if (!rc)
		rc = smc_diag_dump_proto(&smc_proto6, skb, cb);
	return rc;
}

static int smc_diag_handler_dump(struct sk_buff *skb, struct nlmsghdr *h)
{
	struct net *net = sock_net(skb->sk);

	if (h->nlmsg_type == SOCK_DIAG_BY_FAMILY &&
	    h->nlmsg_flags & NLM_F_DUMP) {
		{
			struct netlink_dump_control c = {
				.dump = smc_diag_dump,
				.min_dump_alloc = SKB_WITH_OVERHEAD(32768),
			};
			return netlink_dump_start(net->diag_nlsk, skb, h, &c);
		}
	}
	return 0;
}

static const struct sock_diag_handler smc_diag_handler = {
	.family = AF_SMC,
	.dump = smc_diag_handler_dump,
};

static int __init smc_diag_init(void)
{
	return sock_diag_register(&smc_diag_handler);
}

static void __exit smc_diag_exit(void)
{
	sock_diag_unregister(&smc_diag_handler);
}
Ejemplo n.º 20
0
int ext4_sync_file(struct file *file, loff_t start, loff_t end, int datasync)
{
	struct inode *inode = file->f_mapping->host;
	struct ext4_inode_info *ei = EXT4_I(inode);
	journal_t *journal = EXT4_SB(inode->i_sb)->s_journal;
	int ret = 0, err;
	tid_t commit_tid;
	bool needs_barrier = false;

	J_ASSERT(ext4_journal_current_handle() == NULL);

	trace_ext4_sync_file_enter(file, datasync);

	if (inode->i_sb->s_flags & MS_RDONLY) {
		/* Make sure that we read updated s_mount_flags value */
		smp_rmb();
		if (EXT4_SB(inode->i_sb)->s_mount_flags & EXT4_MF_FS_ABORTED)
			ret = -EROFS;
		goto out;
	}

	if (!journal) {
		ret = generic_file_fsync(file, start, end, datasync);
		if (!ret && !hlist_empty(&inode->i_dentry))
			ret = ext4_sync_parent(inode);
		goto out;
	}

	ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
	if (ret)
		return ret;
	/*
	 * data=writeback,ordered:
	 *  The caller's filemap_fdatawrite()/wait will sync the data.
	 *  Metadata is in the journal, we wait for proper transaction to
	 *  commit here.
	 *
	 * data=journal:
	 *  filemap_fdatawrite won't do anything (the buffers are clean).
	 *  ext4_force_commit will write the file data into the journal and
	 *  will wait on that.
	 *  filemap_fdatawait() will encounter a ton of newly-dirtied pages
	 *  (they were dirtied by commit).  But that's OK - the blocks are
	 *  safe in-journal, which is all fsync() needs to ensure.
	 */
	if (ext4_should_journal_data(inode)) {
		ret = ext4_force_commit(inode->i_sb);
		goto out;
	}

	commit_tid = datasync ? ei->i_datasync_tid : ei->i_sync_tid;
	if (journal->j_flags & JBD2_BARRIER &&
	    !jbd2_trans_will_send_data_barrier(journal, commit_tid))
		needs_barrier = true;
	ret = jbd2_complete_transaction(journal, commit_tid);
	if (needs_barrier) {
		err = blkdev_issue_flush(inode->i_sb->s_bdev, GFP_KERNEL, NULL);
		if (!ret)
			ret = err;
	}
out:
	trace_ext4_sync_file_exit(inode, ret);
	return ret;
}
Ejemplo n.º 21
0
/**
 *	llc_sap_close - close interface for upper layers.
 *	@sap: SAP to be closed.
 *
 *	Close interface function to upper layer. Each one who wants to
 *	close an open SAP (for example NetBEUI) should call this function.
 * 	Removes this sap from the list of saps in the station and then
 * 	frees the memory for this sap.
 */
void llc_sap_close(struct llc_sap *sap)
{
	WARN_ON(!hlist_empty(&sap->sk_list.list));
	llc_del_sap(sap);
	kfree(sap);
}
Ejemplo n.º 22
0
/*
 * Removes a registered user return notifier.  Must be called from atomic
 * context, and from the same cpu registration occured in.
 */
void user_return_notifier_unregister(struct user_return_notifier *urn)
{
	hlist_del(&urn->link);
	if (hlist_empty(&__get_cpu_var(return_notifier_list)))
		clear_tsk_thread_flag(current, TIF_USER_RETURN_NOTIFY);
}
Ejemplo n.º 23
0
/**
 *  udp_lib_get_port  -  UDP/-Lite port lookup for IPv4 and IPv6
 *
 *  @sk:          socket struct in question
 *  @snum:        port number to look up
 *  @saddr_comp:  AF-dependent comparison of bound local IP addresses
 */
int udp_lib_get_port(struct sock *sk, unsigned short snum,
		       int (*saddr_comp)(const struct sock *sk1,
					 const struct sock *sk2 )    )
{
	struct hlist_head *udptable = sk->sk_prot->h.udp_hash;
	struct hlist_node *node;
	struct hlist_head *head;
	struct sock *sk2;
	int    error = 1;
	struct net *net = sock_net(sk);

	write_lock_bh(&udp_hash_lock);

	if (!snum) {
		int i, low, high, remaining;
		unsigned rover, best, best_size_so_far;

		inet_get_local_port_range(&low, &high);
		remaining = (high - low) + 1;

		best_size_so_far = UINT_MAX;
		best = rover = net_random() % remaining + low;

		/* 1st pass: look for empty (or shortest) hash chain */
		for (i = 0; i < UDP_HTABLE_SIZE; i++) {
			int size = 0;

			head = &udptable[udp_hashfn(net, rover)];
			if (hlist_empty(head))
				goto gotit;

			sk_for_each(sk2, node, head) {
				if (++size >= best_size_so_far)
					goto next;
			}
			best_size_so_far = size;
			best = rover;
		next:
			/* fold back if end of range */
			if (++rover > high)
				rover = low + ((rover - low)
					       & (UDP_HTABLE_SIZE - 1));


		}

		/* 2nd pass: find hole in shortest hash chain */
		rover = best;
		for (i = 0; i < (1 << 16) / UDP_HTABLE_SIZE; i++) {
			if (! __udp_lib_lport_inuse(net, rover, udptable))
				goto gotit;
			rover += UDP_HTABLE_SIZE;
			if (rover > high)
				rover = low + ((rover - low)
					       & (UDP_HTABLE_SIZE - 1));
		}


		/* All ports in use! */
		goto fail;

gotit:
		snum = rover;
	} else {
Ejemplo n.º 24
0
/**
 * put_io_context - put a reference of io_context
 * @ioc: io_context to put
 * @locked_q: request_queue the caller is holding queue_lock of (hint)
 *
 * Decrement reference count of @ioc and release it if the count reaches
 * zero.  If the caller is holding queue_lock of a queue, it can indicate
 * that with @locked_q.  This is an optimization hint and the caller is
 * allowed to pass in %NULL even when it's holding a queue_lock.
 */
void put_io_context(struct io_context *ioc, struct request_queue *locked_q)
{
	struct request_queue *last_q = locked_q;
	unsigned long flags;

	if (ioc == NULL)
		return;

	BUG_ON(atomic_long_read(&ioc->refcount) <= 0);
	if (locked_q)
		lockdep_assert_held(locked_q->queue_lock);

	if (!atomic_long_dec_and_test(&ioc->refcount))
		return;

	/*
	 * Destroy @ioc.  This is a bit messy because icq's are chained
	 * from both ioc and queue, and ioc->lock nests inside queue_lock.
	 * The inner ioc->lock should be held to walk our icq_list and then
	 * for each icq the outer matching queue_lock should be grabbed.
	 * ie. We need to do reverse-order double lock dancing.
	 *
	 * Another twist is that we are often called with one of the
	 * matching queue_locks held as indicated by @locked_q, which
	 * prevents performing double-lock dance for other queues.
	 *
	 * So, we do it in two stages.  The fast path uses the queue_lock
	 * the caller is holding and, if other queues need to be accessed,
	 * uses trylock to avoid introducing locking dependency.  This can
	 * handle most cases, especially if @ioc was performing IO on only
	 * single device.
	 *
	 * If trylock doesn't cut it, we defer to @ioc->release_work which
	 * can do all the double-locking dancing.
	 */
	spin_lock_irqsave_nested(&ioc->lock, flags,
				 ioc_release_depth(locked_q));

	while (!hlist_empty(&ioc->icq_list)) {
		struct io_cq *icq = hlist_entry(ioc->icq_list.first,
						struct io_cq, ioc_node);
		struct request_queue *this_q = icq->q;

		if (this_q != last_q) {
			if (last_q && last_q != locked_q)
				spin_unlock(last_q->queue_lock);
			last_q = NULL;

			if (!spin_trylock(this_q->queue_lock))
				break;
			last_q = this_q;
			continue;
		}
		ioc_exit_icq(icq);
	}

	if (last_q && last_q != locked_q)
		spin_unlock(last_q->queue_lock);

	spin_unlock_irqrestore(&ioc->lock, flags);

	/* if no icq is left, we're done; otherwise, kick release_work */
	if (hlist_empty(&ioc->icq_list))
		kmem_cache_free(iocontext_cachep, ioc);
	else
		schedule_work(&ioc->release_work);
}