From b3b792492cfdf6c9aac6cbd9bd10469e279d81c6 Mon Sep 17 00:00:00 2001 From: Alexey Starikovskiy Date: Tue, 11 Mar 2008 13:30:00 -0400 Subject: [PATCH] --- yaml --- r: 87313 b: refs/heads/master c: 2c81ce4c9c37b910210f2640c28e98a0c398dc26 h: refs/heads/master i: 87311: 6acd607431700f22ed11b085473cc9dd9d0a0787 v: v3 --- [refs] | 2 +- trunk/MAINTAINERS | 2 +- trunk/arch/x86/Kconfig | 3 ++ trunk/arch/x86/kernel/ptrace.c | 9 +--- trunk/arch/x86/kernel/signal_64.c | 38 ++------------- trunk/arch/x86/mm/ioremap.c | 2 + trunk/arch/x86/mm/pgtable_32.c | 18 +++---- trunk/drivers/acpi/ec.c | 11 +++++ trunk/drivers/infiniband/core/cm.c | 3 +- trunk/drivers/infiniband/core/fmr_pool.c | 38 +++++++-------- trunk/drivers/infiniband/core/iwcm.c | 5 +- .../infiniband/hw/cxgb3/iwch_provider.c | 5 +- .../drivers/infiniband/ulp/iser/iser_verbs.c | 47 +++++++++---------- trunk/include/asm-x86/pgtable_32.h | 5 +- trunk/kernel/power/snapshot.c | 41 +++------------- trunk/kernel/sched.c | 21 ++++----- 16 files changed, 94 insertions(+), 156 deletions(-) diff --git a/[refs] b/[refs] index 11f92cb08077..8f9791bc7f30 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: a82f7119fd940c1505fc9fdf93d835fa52bc075d +refs/heads/master: 2c81ce4c9c37b910210f2640c28e98a0c398dc26 diff --git a/trunk/MAINTAINERS b/trunk/MAINTAINERS index 25f450fe1059..558636e3a954 100644 --- a/trunk/MAINTAINERS +++ b/trunk/MAINTAINERS @@ -2156,7 +2156,7 @@ L: netdev@vger.kernel.org S: Maintained IPATH DRIVER: -P: Ralph Campbell +P: Arthur Jones M: infinipath@qlogic.com L: general@lists.openfabrics.org T: git git://git.qlogic.com/ipath-linux-2.6 diff --git a/trunk/arch/x86/Kconfig b/trunk/arch/x86/Kconfig index 237fc128143d..f41c9538ca30 100644 --- a/trunk/arch/x86/Kconfig +++ b/trunk/arch/x86/Kconfig @@ -66,6 +66,9 @@ config MMU config ZONE_DMA def_bool y +config QUICKLIST + def_bool X86_32 + config SBUS bool diff --git a/trunk/arch/x86/kernel/ptrace.c b/trunk/arch/x86/kernel/ptrace.c index d5904eef1d31..8f64abe699fd 100644 --- a/trunk/arch/x86/kernel/ptrace.c +++ b/trunk/arch/x86/kernel/ptrace.c @@ -1055,17 +1055,10 @@ static int putreg32(struct task_struct *child, unsigned regno, u32 value) R32(esi, si); R32(ebp, bp); R32(eax, ax); + R32(orig_eax, orig_ax); R32(eip, ip); R32(esp, sp); - case offsetof(struct user32, regs.orig_eax): - /* - * Sign-extend the value so that orig_eax = -1 - * causes (long)orig_ax < 0 tests to fire correctly. - */ - regs->orig_ax = (long) (s32) value; - break; - case offsetof(struct user32, regs.eflags): return set_flags(child, value); diff --git a/trunk/arch/x86/kernel/signal_64.c b/trunk/arch/x86/kernel/signal_64.c index 1c83e5124c65..56b72fb67f9b 100644 --- a/trunk/arch/x86/kernel/signal_64.c +++ b/trunk/arch/x86/kernel/signal_64.c @@ -310,35 +310,6 @@ static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, return -EFAULT; } -/* - * Return -1L or the syscall number that @regs is executing. - */ -static long current_syscall(struct pt_regs *regs) -{ - /* - * We always sign-extend a -1 value being set here, - * so this is always either -1L or a syscall number. - */ - return regs->orig_ax; -} - -/* - * Return a value that is -EFOO if the system call in @regs->orig_ax - * returned an error. This only works for @regs from @current. - */ -static long current_syscall_ret(struct pt_regs *regs) -{ -#ifdef CONFIG_IA32_EMULATION - if (test_thread_flag(TIF_IA32)) - /* - * Sign-extend the value so (int)-EFOO becomes (long)-EFOO - * and will match correctly in comparisons. - */ - return (int) regs->ax; -#endif - return regs->ax; -} - /* * OK, we're invoking a handler */ @@ -356,9 +327,9 @@ handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka, #endif /* Are we from a system call? */ - if (current_syscall(regs) >= 0) { + if ((long)regs->orig_ax >= 0) { /* If so, check system call restarting.. */ - switch (current_syscall_ret(regs)) { + switch (regs->ax) { case -ERESTART_RESTARTBLOCK: case -ERESTARTNOHAND: regs->ax = -EINTR; @@ -455,9 +426,10 @@ static void do_signal(struct pt_regs *regs) } /* Did we come from a system call? */ - if (current_syscall(regs) >= 0) { + if ((long)regs->orig_ax >= 0) { /* Restart the system call - no handlers present */ - switch (current_syscall_ret(regs)) { + long res = regs->ax; + switch (res) { case -ERESTARTNOHAND: case -ERESTARTSYS: case -ERESTARTNOINTR: diff --git a/trunk/arch/x86/mm/ioremap.c b/trunk/arch/x86/mm/ioremap.c index 8fe576baa148..ac3c959e271d 100644 --- a/trunk/arch/x86/mm/ioremap.c +++ b/trunk/arch/x86/mm/ioremap.c @@ -134,6 +134,8 @@ static void __iomem *__ioremap(unsigned long phys_addr, unsigned long size, return NULL; } + WARN_ON_ONCE(page_is_ram(pfn)); + switch (mode) { case IOR_MODE_UNCACHED: default: diff --git a/trunk/arch/x86/mm/pgtable_32.c b/trunk/arch/x86/mm/pgtable_32.c index 2f9e9afcb9f4..73aba7125203 100644 --- a/trunk/arch/x86/mm/pgtable_32.c +++ b/trunk/arch/x86/mm/pgtable_32.c @@ -342,16 +342,12 @@ static void pgd_mop_up_pmds(struct mm_struct *mm, pgd_t *pgdp) pgd_t *pgd_alloc(struct mm_struct *mm) { - pgd_t *pgd = (pgd_t *)__get_free_page(GFP_KERNEL | __GFP_ZERO); + pgd_t *pgd = quicklist_alloc(0, GFP_KERNEL, pgd_ctor); - /* so that alloc_pd can use it */ - mm->pgd = pgd; - if (pgd) - pgd_ctor(pgd); + mm->pgd = pgd; /* so that alloc_pd can use it */ if (pgd && !pgd_prepopulate_pmd(mm, pgd)) { - pgd_dtor(pgd); - free_page((unsigned long)pgd); + quicklist_free(0, pgd_dtor, pgd); pgd = NULL; } @@ -361,8 +357,12 @@ pgd_t *pgd_alloc(struct mm_struct *mm) void pgd_free(struct mm_struct *mm, pgd_t *pgd) { pgd_mop_up_pmds(mm, pgd); - pgd_dtor(pgd); - free_page((unsigned long)pgd); + quicklist_free(0, pgd_dtor, pgd); +} + +void check_pgt_cache(void) +{ + quicklist_trim(0, pgd_dtor, 25, 16); } void __pte_free_tlb(struct mmu_gather *tlb, struct page *pte) diff --git a/trunk/drivers/acpi/ec.c b/trunk/drivers/acpi/ec.c index caf873c14bfb..2c77359dbdc9 100644 --- a/trunk/drivers/acpi/ec.c +++ b/trunk/drivers/acpi/ec.c @@ -129,6 +129,7 @@ static struct acpi_ec { struct mutex lock; wait_queue_head_t wait; struct list_head list; + atomic_t irq_count; u8 handlers_installed; } *boot_ec, *first_ec; @@ -181,6 +182,8 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) { int ret = 0; + atomic_set(&ec->irq_count, 0); + if (unlikely(event == ACPI_EC_EVENT_OBF_1 && test_bit(EC_FLAGS_NO_OBF1_GPE, &ec->flags))) force_poll = 1; @@ -227,6 +230,7 @@ static int acpi_ec_wait(struct acpi_ec *ec, enum ec_event event, int force_poll) while (time_before(jiffies, delay)) { if (acpi_ec_check_status(ec, event)) goto end; + msleep(5); } } pr_err(PREFIX "acpi_ec_wait timeout," @@ -529,6 +533,13 @@ static u32 acpi_ec_gpe_handler(void *data) struct acpi_ec *ec = data; pr_debug(PREFIX "~~~> interrupt\n"); + atomic_inc(&ec->irq_count); + if (atomic_read(&ec->irq_count) > 5) { + pr_err(PREFIX "GPE storm detected, disabling EC GPE\n"); + acpi_disable_gpe(NULL, ec->gpe, ACPI_ISR); + clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); + return ACPI_INTERRUPT_HANDLED; + } clear_bit(EC_FLAGS_WAIT_GPE, &ec->flags); if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) wake_up(&ec->wait); diff --git a/trunk/drivers/infiniband/core/cm.c b/trunk/drivers/infiniband/core/cm.c index 4df405157086..b10ade92efed 100644 --- a/trunk/drivers/infiniband/core/cm.c +++ b/trunk/drivers/infiniband/core/cm.c @@ -3759,7 +3759,6 @@ static void cm_remove_one(struct ib_device *device) port = cm_dev->port[i-1]; ib_modify_port(device, port->port_num, 0, &port_modify); ib_unregister_mad_agent(port->mad_agent); - flush_workqueue(cm.wq); cm_remove_port_fs(port); } kobject_put(&cm_dev->dev_obj); @@ -3814,7 +3813,6 @@ static void __exit ib_cm_cleanup(void) cancel_delayed_work(&timewait_info->work.work); spin_unlock_irq(&cm.lock); - ib_unregister_client(&cm_client); destroy_workqueue(cm.wq); list_for_each_entry_safe(timewait_info, tmp, &cm.timewait_list, list) { @@ -3822,6 +3820,7 @@ static void __exit ib_cm_cleanup(void) kfree(timewait_info); } + ib_unregister_client(&cm_client); class_unregister(&cm_class); idr_destroy(&cm.local_id_table); } diff --git a/trunk/drivers/infiniband/core/fmr_pool.c b/trunk/drivers/infiniband/core/fmr_pool.c index 06d502c06a4d..7f00347364f7 100644 --- a/trunk/drivers/infiniband/core/fmr_pool.c +++ b/trunk/drivers/infiniband/core/fmr_pool.c @@ -139,7 +139,7 @@ static inline struct ib_pool_fmr *ib_fmr_cache_lookup(struct ib_fmr_pool *pool, static void ib_fmr_batch_release(struct ib_fmr_pool *pool) { int ret; - struct ib_pool_fmr *fmr; + struct ib_pool_fmr *fmr, *next; LIST_HEAD(unmap_list); LIST_HEAD(fmr_list); @@ -158,6 +158,20 @@ static void ib_fmr_batch_release(struct ib_fmr_pool *pool) #endif } + /* + * The free_list may hold FMRs that have been put there + * because they haven't reached the max_remap count. + * Invalidate their mapping as well. + */ + list_for_each_entry_safe(fmr, next, &pool->free_list, list) { + if (fmr->remap_count == 0) + continue; + hlist_del_init(&fmr->cache_node); + fmr->remap_count = 0; + list_add_tail(&fmr->fmr->list, &fmr_list); + list_move(&fmr->list, &unmap_list); + } + list_splice(&pool->dirty_list, &unmap_list); INIT_LIST_HEAD(&pool->dirty_list); pool->dirty_len = 0; @@ -370,11 +384,6 @@ void ib_destroy_fmr_pool(struct ib_fmr_pool *pool) i = 0; list_for_each_entry_safe(fmr, tmp, &pool->free_list, list) { - if (fmr->remap_count) { - INIT_LIST_HEAD(&fmr_list); - list_add_tail(&fmr->fmr->list, &fmr_list); - ib_unmap_fmr(&fmr_list); - } ib_dealloc_fmr(fmr->fmr); list_del(&fmr->list); kfree(fmr); @@ -398,23 +407,8 @@ EXPORT_SYMBOL(ib_destroy_fmr_pool); */ int ib_flush_fmr_pool(struct ib_fmr_pool *pool) { - int serial; - struct ib_pool_fmr *fmr, *next; - - /* - * The free_list holds FMRs that may have been used - * but have not been remapped enough times to be dirty. - * Put them on the dirty list now so that the cleanup - * thread will reap them too. - */ - spin_lock_irq(&pool->pool_lock); - list_for_each_entry_safe(fmr, next, &pool->free_list, list) { - if (fmr->remap_count > 0) - list_move(&fmr->list, &pool->dirty_list); - } - spin_unlock_irq(&pool->pool_lock); + int serial = atomic_inc_return(&pool->req_ser); - serial = atomic_inc_return(&pool->req_ser); wake_up_process(pool->thread); if (wait_event_interruptible(pool->force_wait, diff --git a/trunk/drivers/infiniband/core/iwcm.c b/trunk/drivers/infiniband/core/iwcm.c index 81c9195b512a..223b1aa7d92b 100644 --- a/trunk/drivers/infiniband/core/iwcm.c +++ b/trunk/drivers/infiniband/core/iwcm.c @@ -839,7 +839,6 @@ static void cm_work_handler(struct work_struct *_work) unsigned long flags; int empty; int ret = 0; - int destroy_id; spin_lock_irqsave(&cm_id_priv->lock, flags); empty = list_empty(&cm_id_priv->work_list); @@ -858,9 +857,9 @@ static void cm_work_handler(struct work_struct *_work) destroy_cm_id(&cm_id_priv->id); } BUG_ON(atomic_read(&cm_id_priv->refcount)==0); - destroy_id = test_bit(IWCM_F_CALLBACK_DESTROY, &cm_id_priv->flags); if (iwcm_deref_id(cm_id_priv)) { - if (destroy_id) { + if (test_bit(IWCM_F_CALLBACK_DESTROY, + &cm_id_priv->flags)) { BUG_ON(!list_empty(&cm_id_priv->work_list)); free_cm_id(cm_id_priv); } diff --git a/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c b/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c index b2ea9210467f..df1838f8f94d 100644 --- a/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/trunk/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -189,7 +189,7 @@ static struct ib_cq *iwch_create_cq(struct ib_device *ibdev, int entries, int ve return ERR_PTR(-ENOMEM); } chp->rhp = rhp; - chp->ibcq.cqe = 1 << chp->cq.size_log2; + chp->ibcq.cqe = (1 << chp->cq.size_log2) - 1; spin_lock_init(&chp->lock); atomic_set(&chp->refcnt, 1); init_waitqueue_head(&chp->wait); @@ -819,11 +819,8 @@ static struct ib_qp *iwch_create_qp(struct ib_pd *pd, kfree(qhp); return ERR_PTR(-ENOMEM); } - attrs->cap.max_recv_wr = rqsize - 1; attrs->cap.max_send_wr = sqsize; - attrs->cap.max_inline_data = T3_MAX_INLINE; - qhp->rhp = rhp; qhp->attr.pd = php->pdid; qhp->attr.scq = ((struct iwch_cq *) attrs->send_cq)->cq.cqid; diff --git a/trunk/drivers/infiniband/ulp/iser/iser_verbs.c b/trunk/drivers/infiniband/ulp/iser/iser_verbs.c index 993f0a8ff28f..714b8db02b29 100644 --- a/trunk/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/trunk/drivers/infiniband/ulp/iser/iser_verbs.c @@ -237,32 +237,36 @@ static int iser_free_ib_conn_res(struct iser_conn *ib_conn) static struct iser_device *iser_device_find_by_ib_device(struct rdma_cm_id *cma_id) { - struct iser_device *device; + struct list_head *p_list; + struct iser_device *device = NULL; mutex_lock(&ig.device_list_mutex); - list_for_each_entry(device, &ig.device_list, ig_list) + p_list = ig.device_list.next; + while (p_list != &ig.device_list) { + device = list_entry(p_list, struct iser_device, ig_list); /* find if there's a match using the node GUID */ if (device->ib_device->node_guid == cma_id->device->node_guid) - goto inc_refcnt; - - device = kzalloc(sizeof *device, GFP_KERNEL); - if (device == NULL) - goto out; - - /* assign this device to the device */ - device->ib_device = cma_id->device; - /* init the device and link it into ig device list */ - if (iser_create_device_ib_res(device)) { - kfree(device); - device = NULL; - goto out; + break; } - list_add(&device->ig_list, &ig.device_list); -inc_refcnt: - device->refcount++; + if (device == NULL) { + device = kzalloc(sizeof *device, GFP_KERNEL); + if (device == NULL) + goto out; + /* assign this device to the device */ + device->ib_device = cma_id->device; + /* init the device and link it into ig device list */ + if (iser_create_device_ib_res(device)) { + kfree(device); + device = NULL; + goto out; + } + list_add(&device->ig_list, &ig.device_list); + } out: + BUG_ON(device == NULL); + device->refcount++; mutex_unlock(&ig.device_list_mutex); return device; } @@ -368,12 +372,6 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id) int ret; device = iser_device_find_by_ib_device(cma_id); - if (!device) { - iser_err("device lookup/creation failed\n"); - iser_connect_error(cma_id); - return; - } - ib_conn = (struct iser_conn *)cma_id->context; ib_conn->device = device; @@ -382,6 +380,7 @@ static void iser_addr_handler(struct rdma_cm_id *cma_id) iser_err("resolve route failed: %d\n", ret); iser_connect_error(cma_id); } + return; } static void iser_route_handler(struct rdma_cm_id *cma_id) diff --git a/trunk/include/asm-x86/pgtable_32.h b/trunk/include/asm-x86/pgtable_32.h index 4e6a0fca0b47..a842c7222b1e 100644 --- a/trunk/include/asm-x86/pgtable_32.h +++ b/trunk/include/asm-x86/pgtable_32.h @@ -26,9 +26,10 @@ struct mm_struct; struct vm_area_struct; extern pgd_t swapper_pg_dir[1024]; +extern struct kmem_cache *pmd_cache; +void check_pgt_cache(void); -static inline void pgtable_cache_init(void) { } -static inline void check_pgt_cache(void) { } +static inline void pgtable_cache_init(void) {} void paging_init(void); diff --git a/trunk/kernel/power/snapshot.c b/trunk/kernel/power/snapshot.c index 5f91a07c4eac..72a020cabb4c 100644 --- a/trunk/kernel/power/snapshot.c +++ b/trunk/kernel/power/snapshot.c @@ -447,7 +447,7 @@ static void memory_bm_free(struct memory_bitmap *bm, int clear_nosave_free) * of @bm->cur_zone_bm are updated. */ -static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, +static void memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, void **addr, unsigned int *bit_nr) { struct zone_bitmap *zone_bm; @@ -461,8 +461,7 @@ static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, while (pfn < zone_bm->start_pfn || pfn >= zone_bm->end_pfn) { zone_bm = zone_bm->next; - if (!zone_bm) - return -EFAULT; + BUG_ON(!zone_bm); } bm->cur.zone_bm = zone_bm; } @@ -480,40 +479,23 @@ static int memory_bm_find_bit(struct memory_bitmap *bm, unsigned long pfn, pfn -= bb->start_pfn; *bit_nr = pfn % BM_BITS_PER_CHUNK; *addr = bb->data + pfn / BM_BITS_PER_CHUNK; - return 0; } static void memory_bm_set_bit(struct memory_bitmap *bm, unsigned long pfn) { void *addr; unsigned int bit; - int error; - error = memory_bm_find_bit(bm, pfn, &addr, &bit); - BUG_ON(error); + memory_bm_find_bit(bm, pfn, &addr, &bit); set_bit(bit, addr); } -static int mem_bm_set_bit_check(struct memory_bitmap *bm, unsigned long pfn) -{ - void *addr; - unsigned int bit; - int error; - - error = memory_bm_find_bit(bm, pfn, &addr, &bit); - if (!error) - set_bit(bit, addr); - return error; -} - static void memory_bm_clear_bit(struct memory_bitmap *bm, unsigned long pfn) { void *addr; unsigned int bit; - int error; - error = memory_bm_find_bit(bm, pfn, &addr, &bit); - BUG_ON(error); + memory_bm_find_bit(bm, pfn, &addr, &bit); clear_bit(bit, addr); } @@ -521,10 +503,8 @@ static int memory_bm_test_bit(struct memory_bitmap *bm, unsigned long pfn) { void *addr; unsigned int bit; - int error; - error = memory_bm_find_bit(bm, pfn, &addr, &bit); - BUG_ON(error); + memory_bm_find_bit(bm, pfn, &addr, &bit); return test_bit(bit, addr); } @@ -729,15 +709,8 @@ static void mark_nosave_pages(struct memory_bitmap *bm) region->end_pfn << PAGE_SHIFT); for (pfn = region->start_pfn; pfn < region->end_pfn; pfn++) - if (pfn_valid(pfn)) { - /* - * It is safe to ignore the result of - * mem_bm_set_bit_check() here, since we won't - * touch the PFNs for which the error is - * returned anyway. - */ - mem_bm_set_bit_check(bm, pfn); - } + if (pfn_valid(pfn)) + memory_bm_set_bit(bm, pfn); } } diff --git a/trunk/kernel/sched.c b/trunk/kernel/sched.c index 1cb53fb1fe3d..b02e4fc25645 100644 --- a/trunk/kernel/sched.c +++ b/trunk/kernel/sched.c @@ -5813,6 +5813,13 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) /* Must be high prio: stop_machine expects to yield to it. */ rq = task_rq_lock(p, &flags); __setscheduler(rq, p, SCHED_FIFO, MAX_RT_PRIO-1); + + /* Update our root-domain */ + if (rq->rd) { + BUG_ON(!cpu_isset(cpu, rq->rd->span)); + cpu_set(cpu, rq->rd->online); + } + task_rq_unlock(rq, &flags); cpu_rq(cpu)->migration_thread = p; break; @@ -5821,15 +5828,6 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) case CPU_ONLINE_FROZEN: /* Strictly unnecessary, as first user will wake it. */ wake_up_process(cpu_rq(cpu)->migration_thread); - - /* Update our root-domain */ - rq = cpu_rq(cpu); - spin_lock_irqsave(&rq->lock, flags); - if (rq->rd) { - BUG_ON(!cpu_isset(cpu, rq->rd->span)); - cpu_set(cpu, rq->rd->online); - } - spin_unlock_irqrestore(&rq->lock, flags); break; #ifdef CONFIG_HOTPLUG_CPU @@ -5881,8 +5879,7 @@ migration_call(struct notifier_block *nfb, unsigned long action, void *hcpu) spin_unlock_irq(&rq->lock); break; - case CPU_DYING: - case CPU_DYING_FROZEN: + case CPU_DOWN_PREPARE: /* Update our root-domain */ rq = cpu_rq(cpu); spin_lock_irqsave(&rq->lock, flags); @@ -6106,8 +6103,6 @@ static void rq_attach_root(struct rq *rq, struct root_domain *rd) rq->rd = rd; cpu_set(rq->cpu, rd->span); - if (cpu_isset(rq->cpu, cpu_online_map)) - cpu_set(rq->cpu, rd->online); for (class = sched_class_highest; class; class = class->next) { if (class->join_domain)